YYText

Powerful text framework for iOS to display and edit rich text.

Features

  • UILabel and UITextView API compatible
  • High performance asynchronous text layout and rendering
  • Extended CoreText attributes with more text effects
  • Text attachments with UIImage, UIView and CALayer
  • Custom highlight text range to allow user interact with
  • Text parser support (built in markdown/emoticon parser)
  • Text container path and exclusion paths support
  • Vertical form layout support (for CJK text)
  • Image and attributed text copy/paste support
  • Attributed text placeholder support
  • Custom keyboard view support
  • Undo and redo control
  • Attributed text archiver and unarchiver support
  • Multi-language and VoiceOver support
  • Interface Builder support
  • Fully documented

Architecture

YYText vs TextKit

Text Attributes

YYText supported attributes

Demo Attribute Name Class
TextAttachment YYTextAttachment
TextHighlight YYTextHighlight
TextBinding YYTextBinding
TextShadow
TextInnerShadow
YYTextShadow
TextBorder YYTextBorder
TextBackgroundBorder YYTextBorder
TextBlockBorder YYTextBorder
TextGlyphTransform NSValue(CGAffineTransform)
TextUnderline YYTextDecoration
TextStrickthrough YYTextDecoration
TextBackedString YYTextBackedString

CoreText attributes which is supported by YYText

Demo Attribute Name Class
Font UIFont(CTFontRef)
Kern NSNumber
StrokeWidth NSNumber
StrokeColor CGColorRef
Shadow NSShadow
Ligature NSNumber
VerticalGlyphForm NSNumber(BOOL)
WritingDirection NSArray(NSNumber)
RunDelegate CTRunDelegateRef
TextAlignment NSParagraphStyle
(NSTextAlignment)
LineBreakMode NSParagraphStyle
(NSLineBreakMode)
LineSpacing NSParagraphStyle
(CGFloat)
ParagraphSpacing
ParagraphSpacingBefore
NSParagraphStyle
(CGFloat)
FirstLineHeadIndent NSParagraphStyle
(CGFloat)
HeadIndent NSParagraphStyle
(CGFloat)
TailIndent NSParagraphStyle
(CGFloat)
MinimumLineHeight NSParagraphStyle
(CGFloat)
MaximumLineHeight NSParagraphStyle
(CGFloat)
LineHeightMultiple NSParagraphStyle
(CGFloat)
BaseWritingDirection NSParagraphStyle
(NSWritingDirection)
DefaultTabInterval
TabStops
NSParagraphStyle
CGFloat/NSArray(NSTextTab)

Usage

Basic

// YYLabel (similar to UILabel)
YYLabel *label = [YYLabel new];
label.frame = ...
label.font = ...
label.textColor = ...
label.textAlignment = ...
label.lineBreakMode = ...
label.numberOfLines = ...
label.text = ...
    
// YYTextView (similar to UITextView)
YYTextView *textView = [YYTextView new];
textView.frame = ...
textView.font = ...
textView.textColor = ...
textView.dataDetectorTypes = ...
textView.placeHolderText = ...
textView.placeHolderTextColor = ...
textView.delegate = ...

Attributed text

// 1. Create an attributed string.
NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:@"Some Text, blabla..."];
    
// 2. Set attributes to text, you can use almost all CoreText attributes.
text.yy_font = [UIFont boldSystemFontOfSize:30];
text.yy_color = [UIColor blueColor];
[text yy_setColor:[UIColor redColor] range:NSMakeRange(0, 4)];
text.yy_lineSpacing = 10;
    
// 3. Set to YYLabel or YYTextView.
YYLabel *label = [YYLabel new];
label.frame = ...
label.attributedString = text;
    
YYTextView *textView = [YYTextView new];
textView.frame = ...
textView.attributedString = text;

Text highlight

You can use some convenience methods to set text highlight:

[text yy_setTextHighlightRange:range
                       color:[UIColor blueColor]
             backgroundColor:[UIColor grayColor]
                   tapAction:^(UIView *containerView, NSAttributedString *text, NSRange range, CGRect rect){ 
                       NSLog(@"tap text range:..."); 
                   }];

Or set the text highlight with your custom config:

// 1. Create a 'highlight' attribute for text.
YYTextBorder *border = [YYTextBorder borderWithFillColor:[UIColor grayColor] cornerRadius:3];
   
YYTextHighlight *highlight = [YYTextHighlight new];
[highlight setColor:[UIColor whiteColor]];
[highlight setBackgroundBorder:highlightBorder];
highlight.tapAction = ^(UIView *containerView, NSAttributedString *text, NSRange range, CGRect rect) {
 NSLog(@"tap text range:..."); 
 // you can also set the action handler to YYLabel or YYTextView.
};
    
// 2. Add 'highlight' attribute to a range of text.
[attributedText yy_setTextHighlight:highlight range:highlightRange];
    
// 3. Set text to label or text view.
YYLabel *label = ...
label.attributedText = attributedText
    
YYTextView *textView = ...
textView.attributedText = ...
    
// 4. Receive user interactive action.
label.highlightTapAction = ^(UIView *containerView, NSAttributedString *text, NSRange range, CGRect rect) {
   NSLog(@"tap text range:...");
};
label.highlightLongPressAction = ^(UIView *containerView, NSAttributedString *text, NSRange range, CGRect rect) {
   NSLog(@"long press text range:...");
};
    
@UITextViewDelegate
- (void)textView:(YYTextView *)textView didTapHighlight:(YYTextHighlight *)highlight inRange:(NSRange)characterRange rect:(CGRect)rect {
   NSLog(@"tap text range:...");
}
- (void)textView:(YYTextView *)textView didLongPressHighlight:(YYTextHighlight *)highlight inRange:(NSRange)characterRange rect:(CGRect)rect {
   NSLog(@"long press text range:...");
}

Text attachments

NSMutableAttributedString *text = [NSMutableAttributedString new];
UIFont *font = [UIFont systemFontOfSize:16];
NSMutableAttributedString *attachment = nil;
	
// UIImage attachment
UIImage *image = [UIImage imageNamed:@"dribbble64_imageio"];
attachment = [NSMutableAttributedString yy_attachmentStringWithContent:image contentMode:UIViewContentModeCenter attachmentSize:image.size alignToFont:font alignment:YYTextVerticalAlignmentCenter];
[text appendAttributedString: attachment];
	
// UIView attachment
UISwitch *switcher = [UISwitch new];
[switcher sizeToFit];
attachment = [NSMutableAttributedString yy_attachmentStringWithContent:switcher contentMode:UIViewContentModeBottom attachmentSize:switcher.size alignToFont:font alignment:YYTextVerticalAlignmentCenter];
[text appendAttributedString: attachment];
	
// CALayer attachment
CASharpLayer *layer = [CASharpLayer layer];
layer.path = ...
attachment = [NSMutableAttributedString yy_attachmentStringWithContent:layer contentMode:UIViewContentModeBottom attachmentSize:switcher.size alignToFont:font alignment:YYTextVerticalAlignmentCenter];
[text appendAttributedString: attachment];

Text layout calculation

NSAttributedString *text = ...
CGSize size = CGSizeMake(100, CGFLOAT_MAX);
YYTextLayout *layout = [YYTextLayout layoutWithContainerSize:size text:text];
	
// get text bounding
layout.textBoundingRect; // get bounding rect
layout.textBoundingSize; // get bounding size
	
 // query text layout
[layout lineIndexForPoint:CGPointMake(10,10)];
[layout closestLineIndexForPoint:CGPointMake(10,10)];
[layout closestPositionToPoint:CGPointMake(10,10)];
[layout textRangeAtPoint:CGPointMake(10,10)];
[layout rectForRange:[YYTextRange rangeWithRange:NSMakeRange(10,2)]];
[layout selectionRectsForRange:[YYTextRange rangeWithRange:NSMakeRange(10,2)]];
	
// text layout display
YYLabel *label = [YYLabel new];
label.size = layout.textBoundingSize;
label.textLayout = layout;

Adjust text line position

// Convenience methods:
// 1. Create a text line position modifier, implements `YYTextLinePositionModifier` protocol.
// 2. Set it to label or text view.
	
YYTextLinePositionSimpleModifier *modifier = [YYTextLinePositionSimpleModifier new];
modifier.fixedLineHeight = 24;
	
YYLabel *label = [YYLabel new];
label.linePositionModifier = modifier;
	
// Fully control
YYTextLinePositionSimpleModifier *modifier = [YYTextLinePositionSimpleModifier new];
modifier.fixedLineHeight = 24;
	
YYTextContainer *container = [YYTextContainer new];
container.size = CGSizeMake(100, CGFLOAT_MAX);
container.linePositionModifier = modifier;
	
YYTextLayout *layout = [YYTextLayout layoutWithContainer:container text:text];
YYLabel *label = [YYLabel new];
label.size = layout.textBoundingSize;
label.textLayout = layout;

Asynchronous layout and rendering

// If you have performance issues,
// you may enable the asynchronous display mode.
YYLabel *label = ...
label.displaysAsynchronously = YES;
    
// If you want to get the highest performance, you should do 
// text layout with `YYTextLayout` class in background thread.
YYLabel *label = [YYLabel new];
label.displaysAsynchronously = YES;
label.ignoreCommonProperties = YES;
    
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
   // Create attributed string.
   NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:@"Some Text"];
   text.yy_font = [UIFont systemFontOfSize:16];
   text.yy_color = [UIColor grayColor];
   [text yy_setColor:[UIColor redColor] range:NSMakeRange(0, 4)];
 	
   // Create text container
   YYTextContainer *container = [YYTextContainer new];
   container.size = CGSizeMake(100, CGFLOAT_MAX);
   container.maximumNumberOfRows = 0;
   
   // Generate a text layout.
   YYTextLayout *layout = [YYTextLayout layoutWithContainer:container text:text];
   
   dispatch_async(dispatch_get_main_queue(), ^{
       label.size = layout.textBoundingSize;
       label.textLayout = layout;
   });
});

Text container control

YYLabel *label = ...
label.textContainerPath = [UIBezierPath bezierPathWith...];
label.exclusionPaths = 	@[[UIBezierPath bezierPathWith...];,...];
label.textContainerInset = UIEdgeInsetsMake(...);
label.verticalForm = YES/NO;
    
YYTextView *textView = ...
textView.exclusionPaths = 	@[[UIBezierPath bezierPathWith...];,...];
textView.textContainerInset = UIEdgeInsetsMake(...);
textView.verticalForm = YES/NO;

Text parser

// 1. Create a text parser
	
YYTextSimpleEmoticonParser *parser = [YYTextSimpleEmoticonParser new];
NSMutableDictionary *mapper = [NSMutableDictionary new];
mapper[@":smile:"] = [UIImage imageNamed:@"smile.png"];
mapper[@":cool:"] = [UIImage imageNamed:@"cool.png"];
mapper[@":cry:"] = [UIImage imageNamed:@"cry.png"];
mapper[@":wink:"] = [UIImage imageNamed:@"wink.png"];
parser.emoticonMapper = mapper;
	
YYTextSimpleMarkdownParser *parser = [YYTextSimpleMarkdownParser new];
[parser setColorWithDarkTheme];
    
MyCustomParser *parser = ... // custom parser
    
// 2. Attach parser to label or text view
YYLabel *label = ...
label.textParser = parser;
    
YYTextView *textView = ...
textView.textParser = parser;

Debug

// Set a shared debug option to show text layout result.
YYTextDebugOption *debugOptions = [YYTextDebugOption new];
debugOptions.baselineColor = [UIColor redColor];
debugOptions.CTFrameBorderColor = [UIColor redColor];
debugOptions.CTLineFillColor = [UIColor colorWithRed:0.000 green:0.463 blue:1.000 alpha:0.180];
debugOptions.CGGlyphBorderColor = [UIColor colorWithRed:1.000 green:0.524 blue:0.000 alpha:0.200];
[YYTextDebugOption setSharedDebugOption:debugOptions];

More examples

See Demo/YYTextDemo.xcodeproj for more examples:



Installation

CocoaPods

  1. Add pod 'YYText' to your Podfile.
  2. Run pod install or pod update.
  3. Import <YYText/YYText.h>.

Carthage

  1. Add github "ibireme/YYText" to your Cartfile.
  2. Run carthage update --platform ios and add the framework to your project.
  3. Import <YYText/YYText.h>.

Manually

  1. Download all the files in the YYText subdirectory.
  2. Add the source files to your Xcode project.
  3. Link with required frameworks:
    • UIKit
    • CoreFoundation
    • CoreText
    • QuartzCore
    • Accelerate
    • MobileCoreServices
  4. Import YYText.h.

Notice

You may add YYImage or YYWebImage to your project if you want to support animated image (GIF/APNG/WebP).

Documentation

Full API documentation is available on CocoaDocs.

You can also install documentation locally using appledoc.

Requirements

This library requires iOS 6.0+ and Xcode 8.0+.

GitHub

https://github.com/ibireme/YYText