From 7b02207537d35bfa1714bf8beafc921f717d100a Mon Sep 17 00:00:00 2001 From: 单军华 Date: Wed, 11 Jul 2018 10:47:42 +0800 Subject: [PATCH] 首次上传 --- screendisplay/Pods/YYText/YYText/Utility/NSAttributedString+YYText.m | 1404 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 1,404 insertions(+), 0 deletions(-) diff --git a/screendisplay/Pods/YYText/YYText/Utility/NSAttributedString+YYText.m b/screendisplay/Pods/YYText/YYText/Utility/NSAttributedString+YYText.m new file mode 100755 index 0000000..63f9579 --- /dev/null +++ b/screendisplay/Pods/YYText/YYText/Utility/NSAttributedString+YYText.m @@ -0,0 +1,1404 @@ +// +// NSAttributedString+YYText.m +// YYText <https://github.com/ibireme/YYText> +// +// Created by ibireme on 14/10/7. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import "NSAttributedString+YYText.h" +#import "NSParagraphStyle+YYText.h" +#import "YYTextArchiver.h" +#import "YYTextRunDelegate.h" +#import "YYTextUtilities.h" +#import <CoreFoundation/CoreFoundation.h> + + +// Dummy class for category +@interface NSAttributedString_YYText : NSObject @end +@implementation NSAttributedString_YYText @end + + +static double _YYDeviceSystemVersion() { + static double version; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + version = [UIDevice currentDevice].systemVersion.doubleValue; + }); + return version; +} + +#ifndef kSystemVersion +#define kSystemVersion _YYDeviceSystemVersion() +#endif + +#ifndef kiOS6Later +#define kiOS6Later (kSystemVersion >= 6) +#endif + +#ifndef kiOS7Later +#define kiOS7Later (kSystemVersion >= 7) +#endif + +#ifndef kiOS8Later +#define kiOS8Later (kSystemVersion >= 8) +#endif + +#ifndef kiOS9Later +#define kiOS9Later (kSystemVersion >= 9) +#endif + + + +@implementation NSAttributedString (YYText) + +- (NSData *)yy_archiveToData { + NSData *data = nil; + @try { + data = [YYTextArchiver archivedDataWithRootObject:self]; + } + @catch (NSException *exception) { + NSLog(@"%@",exception); + } + return data; +} + ++ (instancetype)yy_unarchiveFromData:(NSData *)data { + NSAttributedString *one = nil; + @try { + one = [YYTextUnarchiver unarchiveObjectWithData:data]; + } + @catch (NSException *exception) { + NSLog(@"%@",exception); + } + return one; +} + +- (NSDictionary *)yy_attributesAtIndex:(NSUInteger)index { + if (index > self.length || self.length == 0) return nil; + if (self.length > 0 && index == self.length) index--; + return [self attributesAtIndex:index effectiveRange:NULL]; +} + +- (id)yy_attribute:(NSString *)attributeName atIndex:(NSUInteger)index { + if (!attributeName) return nil; + if (index > self.length || self.length == 0) return nil; + if (self.length > 0 && index == self.length) index--; + return [self attribute:attributeName atIndex:index effectiveRange:NULL]; +} + +- (NSDictionary *)yy_attributes { + return [self yy_attributesAtIndex:0]; +} + +- (UIFont *)yy_font { + return [self yy_fontAtIndex:0]; +} + +- (UIFont *)yy_fontAtIndex:(NSUInteger)index { + /* + In iOS7 and later, UIFont is toll-free bridged to CTFontRef, + although Apple does not mention it in documentation. + + In iOS6, UIFont is a wrapper for CTFontRef, so CoreText can alse use UIfont, + but UILabel/UITextView cannot use CTFontRef. + + We use UIFont for both CoreText and UIKit. + */ + UIFont *font = [self yy_attribute:NSFontAttributeName atIndex:index]; + if (kSystemVersion <= 6) { + if (font) { + if (CFGetTypeID((__bridge CFTypeRef)(font)) == CTFontGetTypeID()) { + CTFontRef CTFont = (__bridge CTFontRef)(font); + CFStringRef name = CTFontCopyPostScriptName(CTFont); + CGFloat size = CTFontGetSize(CTFont); + if (!name) { + font = nil; + } else { + font = [UIFont fontWithName:(__bridge NSString *)(name) size:size]; + CFRelease(name); + } + } + } + } + return font; +} + +- (NSNumber *)yy_kern { + return [self yy_kernAtIndex:0]; +} + +- (NSNumber *)yy_kernAtIndex:(NSUInteger)index { + return [self yy_attribute:NSKernAttributeName atIndex:index]; +} + +- (UIColor *)yy_color { + return [self yy_colorAtIndex:0]; +} + +- (UIColor *)yy_colorAtIndex:(NSUInteger)index { + UIColor *color = [self yy_attribute:NSForegroundColorAttributeName atIndex:index]; + if (!color) { + CGColorRef ref = (__bridge CGColorRef)([self yy_attribute:(NSString *)kCTForegroundColorAttributeName atIndex:index]); + color = [UIColor colorWithCGColor:ref]; + } + if (color && ![color isKindOfClass:[UIColor class]]) { + if (CFGetTypeID((__bridge CFTypeRef)(color)) == CGColorGetTypeID()) { + color = [UIColor colorWithCGColor:(__bridge CGColorRef)(color)]; + } else { + color = nil; + } + } + return color; +} + +- (UIColor *)yy_backgroundColor { + return [self yy_backgroundColorAtIndex:0]; +} + +- (UIColor *)yy_backgroundColorAtIndex:(NSUInteger)index { + return [self yy_attribute:NSBackgroundColorAttributeName atIndex:index]; +} + +- (NSNumber *)yy_strokeWidth { + return [self yy_strokeWidthAtIndex:0]; +} + +- (NSNumber *)yy_strokeWidthAtIndex:(NSUInteger)index { + return [self yy_attribute:NSStrokeWidthAttributeName atIndex:index]; +} + +- (UIColor *)yy_strokeColor { + return [self yy_strokeColorAtIndex:0]; +} + +- (UIColor *)yy_strokeColorAtIndex:(NSUInteger)index { + UIColor *color = [self yy_attribute:NSStrokeColorAttributeName atIndex:index]; + if (!color) { + CGColorRef ref = (__bridge CGColorRef)([self yy_attribute:(NSString *)kCTStrokeColorAttributeName atIndex:index]); + color = [UIColor colorWithCGColor:ref]; + } + return color; +} + +- (NSShadow *)yy_shadow { + return [self yy_shadowAtIndex:0]; +} + +- (NSShadow *)yy_shadowAtIndex:(NSUInteger)index { + return [self yy_attribute:NSShadowAttributeName atIndex:index]; +} + +- (NSUnderlineStyle)yy_strikethroughStyle { + return [self yy_strikethroughStyleAtIndex:0]; +} + +- (NSUnderlineStyle)yy_strikethroughStyleAtIndex:(NSUInteger)index { + NSNumber *style = [self yy_attribute:NSStrikethroughStyleAttributeName atIndex:index]; + return style.integerValue; +} + +- (UIColor *)yy_strikethroughColor { + return [self yy_strikethroughColorAtIndex:0]; +} + +- (UIColor *)yy_strikethroughColorAtIndex:(NSUInteger)index { + if (kSystemVersion >= 7) { + return [self yy_attribute:NSStrikethroughColorAttributeName atIndex:index]; + } + return nil; +} + +- (NSUnderlineStyle)yy_underlineStyle { + return [self yy_underlineStyleAtIndex:0]; +} + +- (NSUnderlineStyle)yy_underlineStyleAtIndex:(NSUInteger)index { + NSNumber *style = [self yy_attribute:NSUnderlineStyleAttributeName atIndex:index]; + return style.integerValue; +} + +- (UIColor *)yy_underlineColor { + return [self yy_underlineColorAtIndex:0]; +} + +- (UIColor *)yy_underlineColorAtIndex:(NSUInteger)index { + UIColor *color = nil; + if (kSystemVersion >= 7) { + color = [self yy_attribute:NSUnderlineColorAttributeName atIndex:index]; + } + if (!color) { + CGColorRef ref = (__bridge CGColorRef)([self yy_attribute:(NSString *)kCTUnderlineColorAttributeName atIndex:index]); + color = [UIColor colorWithCGColor:ref]; + } + return color; +} + +- (NSNumber *)yy_ligature { + return [self yy_ligatureAtIndex:0]; +} + +- (NSNumber *)yy_ligatureAtIndex:(NSUInteger)index { + return [self yy_attribute:NSLigatureAttributeName atIndex:index]; +} + +- (NSString *)yy_textEffect { + return [self yy_textEffectAtIndex:0]; +} + +- (NSString *)yy_textEffectAtIndex:(NSUInteger)index { + if (kSystemVersion >= 7) { + return [self yy_attribute:NSTextEffectAttributeName atIndex:index]; + } + return nil; +} + +- (NSNumber *)yy_obliqueness { + return [self yy_obliquenessAtIndex:0]; +} + +- (NSNumber *)yy_obliquenessAtIndex:(NSUInteger)index { + if (kSystemVersion >= 7) { + return [self yy_attribute:NSObliquenessAttributeName atIndex:index]; + } + return nil; +} + +- (NSNumber *)yy_expansion { + return [self yy_expansionAtIndex:0]; +} + +- (NSNumber *)yy_expansionAtIndex:(NSUInteger)index { + if (kSystemVersion >= 7) { + return [self yy_attribute:NSExpansionAttributeName atIndex:index]; + } + return nil; +} + +- (NSNumber *)yy_baselineOffset { + return [self yy_baselineOffsetAtIndex:0]; +} + +- (NSNumber *)yy_baselineOffsetAtIndex:(NSUInteger)index { + if (kSystemVersion >= 7) { + return [self yy_attribute:NSBaselineOffsetAttributeName atIndex:index]; + } + return nil; +} + +- (BOOL)yy_verticalGlyphForm { + return [self yy_verticalGlyphFormAtIndex:0]; +} + +- (BOOL)yy_verticalGlyphFormAtIndex:(NSUInteger)index { + NSNumber *num = [self yy_attribute:NSVerticalGlyphFormAttributeName atIndex:index]; + return num.boolValue; +} + +- (NSString *)yy_language { + return [self yy_languageAtIndex:0]; +} + +- (NSString *)yy_languageAtIndex:(NSUInteger)index { + if (kSystemVersion >= 7) { + return [self yy_attribute:(id)kCTLanguageAttributeName atIndex:index]; + } + return nil; +} + +- (NSArray *)yy_writingDirection { + return [self yy_writingDirectionAtIndex:0]; +} + +- (NSArray *)yy_writingDirectionAtIndex:(NSUInteger)index { + return [self yy_attribute:(id)kCTWritingDirectionAttributeName atIndex:index]; +} + +- (NSParagraphStyle *)yy_paragraphStyle { + return [self yy_paragraphStyleAtIndex:0]; +} + +- (NSParagraphStyle *)yy_paragraphStyleAtIndex:(NSUInteger)index { + /* + NSParagraphStyle is NOT toll-free bridged to CTParagraphStyleRef. + + CoreText can use both NSParagraphStyle and CTParagraphStyleRef, + but UILabel/UITextView can only use NSParagraphStyle. + + We use NSParagraphStyle in both CoreText and UIKit. + */ + NSParagraphStyle *style = [self yy_attribute:NSParagraphStyleAttributeName atIndex:index]; + if (style) { + if (CFGetTypeID((__bridge CFTypeRef)(style)) == CTParagraphStyleGetTypeID()) { \ + style = [NSParagraphStyle yy_styleWithCTStyle:(__bridge CTParagraphStyleRef)(style)]; + } + } + return style; +} + +#define ParagraphAttribute(_attr_) \ +NSParagraphStyle *style = self.yy_paragraphStyle; \ +if (!style) style = [NSParagraphStyle defaultParagraphStyle]; \ +return style. _attr_; + +#define ParagraphAttributeAtIndex(_attr_) \ +NSParagraphStyle *style = [self yy_paragraphStyleAtIndex:index]; \ +if (!style) style = [NSParagraphStyle defaultParagraphStyle]; \ +return style. _attr_; + +- (NSTextAlignment)yy_alignment { + ParagraphAttribute(alignment); +} + +- (NSLineBreakMode)yy_lineBreakMode { + ParagraphAttribute(lineBreakMode); +} + +- (CGFloat)yy_lineSpacing { + ParagraphAttribute(lineSpacing); +} + +- (CGFloat)yy_paragraphSpacing { + ParagraphAttribute(paragraphSpacing); +} + +- (CGFloat)yy_paragraphSpacingBefore { + ParagraphAttribute(paragraphSpacingBefore); +} + +- (CGFloat)yy_firstLineHeadIndent { + ParagraphAttribute(firstLineHeadIndent); +} + +- (CGFloat)yy_headIndent { + ParagraphAttribute(headIndent); +} + +- (CGFloat)yy_tailIndent { + ParagraphAttribute(tailIndent); +} + +- (CGFloat)yy_minimumLineHeight { + ParagraphAttribute(minimumLineHeight); +} + +- (CGFloat)yy_maximumLineHeight { + ParagraphAttribute(maximumLineHeight); +} + +- (CGFloat)yy_lineHeightMultiple { + ParagraphAttribute(lineHeightMultiple); +} + +- (NSWritingDirection)yy_baseWritingDirection { + ParagraphAttribute(baseWritingDirection); +} + +- (float)yy_hyphenationFactor { + ParagraphAttribute(hyphenationFactor); +} + +- (CGFloat)yy_defaultTabInterval { + if (!kiOS7Later) return 0; + ParagraphAttribute(defaultTabInterval); +} + +- (NSArray *)yy_tabStops { + if (!kiOS7Later) return nil; + ParagraphAttribute(tabStops); +} + +- (NSTextAlignment)yy_alignmentAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(alignment); +} + +- (NSLineBreakMode)yy_lineBreakModeAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(lineBreakMode); +} + +- (CGFloat)yy_lineSpacingAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(lineSpacing); +} + +- (CGFloat)yy_paragraphSpacingAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(paragraphSpacing); +} + +- (CGFloat)yy_paragraphSpacingBeforeAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(paragraphSpacingBefore); +} + +- (CGFloat)yy_firstLineHeadIndentAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(firstLineHeadIndent); +} + +- (CGFloat)yy_headIndentAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(headIndent); +} + +- (CGFloat)yy_tailIndentAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(tailIndent); +} + +- (CGFloat)yy_minimumLineHeightAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(minimumLineHeight); +} + +- (CGFloat)yy_maximumLineHeightAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(maximumLineHeight); +} + +- (CGFloat)yy_lineHeightMultipleAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(lineHeightMultiple); +} + +- (NSWritingDirection)yy_baseWritingDirectionAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(baseWritingDirection); +} + +- (float)yy_hyphenationFactorAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(hyphenationFactor); +} + +- (CGFloat)yy_defaultTabIntervalAtIndex:(NSUInteger)index { + if (!kiOS7Later) return 0; + ParagraphAttributeAtIndex(defaultTabInterval); +} + +- (NSArray *)yy_tabStopsAtIndex:(NSUInteger)index { + if (!kiOS7Later) return nil; + ParagraphAttributeAtIndex(tabStops); +} + +#undef ParagraphAttribute +#undef ParagraphAttributeAtIndex + +- (YYTextShadow *)yy_textShadow { + return [self yy_textShadowAtIndex:0]; +} + +- (YYTextShadow *)yy_textShadowAtIndex:(NSUInteger)index { + return [self yy_attribute:YYTextShadowAttributeName atIndex:index]; +} + +- (YYTextShadow *)yy_textInnerShadow { + return [self yy_textInnerShadowAtIndex:0]; +} + +- (YYTextShadow *)yy_textInnerShadowAtIndex:(NSUInteger)index { + return [self yy_attribute:YYTextInnerShadowAttributeName atIndex:index]; +} + +- (YYTextDecoration *)yy_textUnderline { + return [self yy_textUnderlineAtIndex:0]; +} + +- (YYTextDecoration *)yy_textUnderlineAtIndex:(NSUInteger)index { + return [self yy_attribute:YYTextUnderlineAttributeName atIndex:index]; +} + +- (YYTextDecoration *)yy_textStrikethrough { + return [self yy_textStrikethroughAtIndex:0]; +} + +- (YYTextDecoration *)yy_textStrikethroughAtIndex:(NSUInteger)index { + return [self yy_attribute:YYTextStrikethroughAttributeName atIndex:index]; +} + +- (YYTextBorder *)yy_textBorder { + return [self yy_textBorderAtIndex:0]; +} + +- (YYTextBorder *)yy_textBorderAtIndex:(NSUInteger)index { + return [self yy_attribute:YYTextBorderAttributeName atIndex:index]; +} + +- (YYTextBorder *)yy_textBackgroundBorder { + return [self yy_textBackgroundBorderAtIndex:0]; +} + +- (YYTextBorder *)yy_textBackgroundBorderAtIndex:(NSUInteger)index { + return [self yy_attribute:YYTextBackedStringAttributeName atIndex:index]; +} + +- (CGAffineTransform)yy_textGlyphTransform { + return [self yy_textGlyphTransformAtIndex:0]; +} + +- (CGAffineTransform)yy_textGlyphTransformAtIndex:(NSUInteger)index { + NSValue *value = [self yy_attribute:YYTextGlyphTransformAttributeName atIndex:index]; + if (!value) return CGAffineTransformIdentity; + return [value CGAffineTransformValue]; +} + +- (NSString *)yy_plainTextForRange:(NSRange)range { + if (range.location == NSNotFound ||range.length == NSNotFound) return nil; + NSMutableString *result = [NSMutableString string]; + if (range.length == 0) return result; + NSString *string = self.string; + [self enumerateAttribute:YYTextBackedStringAttributeName inRange:range options:kNilOptions usingBlock:^(id value, NSRange range, BOOL *stop) { + YYTextBackedString *backed = value; + if (backed && backed.string) { + [result appendString:backed.string]; + } else { + [result appendString:[string substringWithRange:range]]; + } + }]; + return result; +} + ++ (NSMutableAttributedString *)yy_attachmentStringWithContent:(id)content + contentMode:(UIViewContentMode)contentMode + width:(CGFloat)width + ascent:(CGFloat)ascent + descent:(CGFloat)descent { + NSMutableAttributedString *atr = [[NSMutableAttributedString alloc] initWithString:YYTextAttachmentToken]; + + YYTextAttachment *attach = [YYTextAttachment new]; + attach.content = content; + attach.contentMode = contentMode; + [atr yy_setTextAttachment:attach range:NSMakeRange(0, atr.length)]; + + YYTextRunDelegate *delegate = [YYTextRunDelegate new]; + delegate.width = width; + delegate.ascent = ascent; + delegate.descent = descent; + CTRunDelegateRef delegateRef = delegate.CTRunDelegate; + [atr yy_setRunDelegate:delegateRef range:NSMakeRange(0, atr.length)]; + if (delegate) CFRelease(delegateRef); + + return atr; +} + ++ (NSMutableAttributedString *)yy_attachmentStringWithContent:(id)content + contentMode:(UIViewContentMode)contentMode + attachmentSize:(CGSize)attachmentSize + alignToFont:(UIFont *)font + alignment:(YYTextVerticalAlignment)alignment { + NSMutableAttributedString *atr = [[NSMutableAttributedString alloc] initWithString:YYTextAttachmentToken]; + + YYTextAttachment *attach = [YYTextAttachment new]; + attach.content = content; + attach.contentMode = contentMode; + [atr yy_setTextAttachment:attach range:NSMakeRange(0, atr.length)]; + + YYTextRunDelegate *delegate = [YYTextRunDelegate new]; + delegate.width = attachmentSize.width; + switch (alignment) { + case YYTextVerticalAlignmentTop: { + delegate.ascent = font.ascender; + delegate.descent = attachmentSize.height - font.ascender; + if (delegate.descent < 0) { + delegate.descent = 0; + delegate.ascent = attachmentSize.height; + } + } break; + case YYTextVerticalAlignmentCenter: { + CGFloat fontHeight = font.ascender - font.descender; + CGFloat yOffset = font.ascender - fontHeight * 0.5; + delegate.ascent = attachmentSize.height * 0.5 + yOffset; + delegate.descent = attachmentSize.height - delegate.ascent; + if (delegate.descent < 0) { + delegate.descent = 0; + delegate.ascent = attachmentSize.height; + } + } break; + case YYTextVerticalAlignmentBottom: { + delegate.ascent = attachmentSize.height + font.descender; + delegate.descent = -font.descender; + if (delegate.ascent < 0) { + delegate.ascent = 0; + delegate.descent = attachmentSize.height; + } + } break; + default: { + delegate.ascent = attachmentSize.height; + delegate.descent = 0; + } break; + } + + CTRunDelegateRef delegateRef = delegate.CTRunDelegate; + [atr yy_setRunDelegate:delegateRef range:NSMakeRange(0, atr.length)]; + if (delegate) CFRelease(delegateRef); + + return atr; +} + ++ (NSMutableAttributedString *)yy_attachmentStringWithEmojiImage:(UIImage *)image + fontSize:(CGFloat)fontSize { + if (!image || fontSize <= 0) return nil; + + BOOL hasAnim = NO; + if (image.images.count > 1) { + hasAnim = YES; + } else if (NSProtocolFromString(@"YYAnimatedImage") && + [image conformsToProtocol:NSProtocolFromString(@"YYAnimatedImage")]) { + NSNumber *frameCount = [image valueForKey:@"animatedImageFrameCount"]; + if (frameCount.intValue > 1) hasAnim = YES; + } + + CGFloat ascent = YYTextEmojiGetAscentWithFontSize(fontSize); + CGFloat descent = YYTextEmojiGetDescentWithFontSize(fontSize); + CGRect bounding = YYTextEmojiGetGlyphBoundingRectWithFontSize(fontSize); + + YYTextRunDelegate *delegate = [YYTextRunDelegate new]; + delegate.ascent = ascent; + delegate.descent = descent; + delegate.width = bounding.size.width + 2 * bounding.origin.x; + + YYTextAttachment *attachment = [YYTextAttachment new]; + attachment.contentMode = UIViewContentModeScaleAspectFit; + attachment.contentInsets = UIEdgeInsetsMake(ascent - (bounding.size.height + bounding.origin.y), bounding.origin.x, descent + bounding.origin.y, bounding.origin.x); + if (hasAnim) { + Class imageClass = NSClassFromString(@"YYAnimatedImageView"); + if (!imageClass) imageClass = [UIImageView class]; + UIImageView *view = (id)[imageClass new]; + view.frame = bounding; + view.image = image; + view.contentMode = UIViewContentModeScaleAspectFit; + attachment.content = view; + } else { + attachment.content = image; + } + + NSMutableAttributedString *atr = [[NSMutableAttributedString alloc] initWithString:YYTextAttachmentToken]; + [atr yy_setTextAttachment:attachment range:NSMakeRange(0, atr.length)]; + CTRunDelegateRef ctDelegate = delegate.CTRunDelegate; + [atr yy_setRunDelegate:ctDelegate range:NSMakeRange(0, atr.length)]; + if (ctDelegate) CFRelease(ctDelegate); + + return atr; +} + +- (NSRange)yy_rangeOfAll { + return NSMakeRange(0, self.length); +} + +- (BOOL)yy_isSharedAttributesInAllRange { + __block BOOL shared = YES; + __block NSDictionary *firstAttrs = nil; + [self enumerateAttributesInRange:self.yy_rangeOfAll options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { + if (range.location == 0) { + firstAttrs = attrs; + } else { + if (firstAttrs.count != attrs.count) { + shared = NO; + *stop = YES; + } else if (firstAttrs) { + if (![firstAttrs isEqualToDictionary:attrs]) { + shared = NO; + *stop = YES; + } + } + } + }]; + return shared; +} + +- (BOOL)yy_canDrawWithUIKit { + static NSMutableSet *failSet; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + failSet = [NSMutableSet new]; + [failSet addObject:(id)kCTGlyphInfoAttributeName]; + [failSet addObject:(id)kCTCharacterShapeAttributeName]; + if (kiOS7Later) { + [failSet addObject:(id)kCTLanguageAttributeName]; + } + [failSet addObject:(id)kCTRunDelegateAttributeName]; + [failSet addObject:(id)kCTBaselineClassAttributeName]; + [failSet addObject:(id)kCTBaselineInfoAttributeName]; + [failSet addObject:(id)kCTBaselineReferenceInfoAttributeName]; + if (kiOS8Later) { + [failSet addObject:(id)kCTRubyAnnotationAttributeName]; + } + [failSet addObject:YYTextShadowAttributeName]; + [failSet addObject:YYTextInnerShadowAttributeName]; + [failSet addObject:YYTextUnderlineAttributeName]; + [failSet addObject:YYTextStrikethroughAttributeName]; + [failSet addObject:YYTextBorderAttributeName]; + [failSet addObject:YYTextBackgroundBorderAttributeName]; + [failSet addObject:YYTextBlockBorderAttributeName]; + [failSet addObject:YYTextAttachmentAttributeName]; + [failSet addObject:YYTextHighlightAttributeName]; + [failSet addObject:YYTextGlyphTransformAttributeName]; + }); + +#define Fail { result = NO; *stop = YES; return; } + __block BOOL result = YES; + [self enumerateAttributesInRange:self.yy_rangeOfAll options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { + if (attrs.count == 0) return; + for (NSString *str in attrs.allKeys) { + if ([failSet containsObject:str]) Fail; + } + if (!kiOS7Later) { + UIFont *font = attrs[NSFontAttributeName]; + if (CFGetTypeID((__bridge CFTypeRef)(font)) == CTFontGetTypeID()) Fail; + } + if (attrs[(id)kCTForegroundColorAttributeName] && !attrs[NSForegroundColorAttributeName]) Fail; + if (attrs[(id)kCTStrokeColorAttributeName] && !attrs[NSStrokeColorAttributeName]) Fail; + if (attrs[(id)kCTUnderlineColorAttributeName]) { + if (!kiOS7Later) Fail; + if (!attrs[NSUnderlineColorAttributeName]) Fail; + } + NSParagraphStyle *style = attrs[NSParagraphStyleAttributeName]; + if (style && CFGetTypeID((__bridge CFTypeRef)(style)) == CTParagraphStyleGetTypeID()) Fail; + }]; + return result; +#undef Fail +} + +@end + +@implementation NSMutableAttributedString (YYText) + +- (void)yy_setAttributes:(NSDictionary *)attributes { + [self setYy_attributes:attributes]; +} + +- (void)setYy_attributes:(NSDictionary *)attributes { + if (attributes == (id)[NSNull null]) attributes = nil; + [self setAttributes:@{} range:NSMakeRange(0, self.length)]; + [attributes enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + [self yy_setAttribute:key value:obj]; + }]; +} + +- (void)yy_setAttribute:(NSString *)name value:(id)value { + [self yy_setAttribute:name value:value range:NSMakeRange(0, self.length)]; +} + +- (void)yy_setAttribute:(NSString *)name value:(id)value range:(NSRange)range { + if (!name || [NSNull isEqual:name]) return; + if (value && ![NSNull isEqual:value]) [self addAttribute:name value:value range:range]; + else [self removeAttribute:name range:range]; +} + +- (void)yy_removeAttributesInRange:(NSRange)range { + [self setAttributes:nil range:range]; +} + +#pragma mark - Property Setter + +- (void)setYy_font:(UIFont *)font { + /* + In iOS7 and later, UIFont is toll-free bridged to CTFontRef, + although Apple does not mention it in documentation. + + In iOS6, UIFont is a wrapper for CTFontRef, so CoreText can alse use UIfont, + but UILabel/UITextView cannot use CTFontRef. + + We use UIFont for both CoreText and UIKit. + */ + [self yy_setFont:font range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_kern:(NSNumber *)kern { + [self yy_setKern:kern range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_color:(UIColor *)color { + [self yy_setColor:color range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_backgroundColor:(UIColor *)backgroundColor { + [self yy_setBackgroundColor:backgroundColor range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_strokeWidth:(NSNumber *)strokeWidth { + [self yy_setStrokeWidth:strokeWidth range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_strokeColor:(UIColor *)strokeColor { + [self yy_setStrokeColor:strokeColor range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_shadow:(NSShadow *)shadow { + [self yy_setShadow:shadow range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_strikethroughStyle:(NSUnderlineStyle)strikethroughStyle { + [self yy_setStrikethroughStyle:strikethroughStyle range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_strikethroughColor:(UIColor *)strikethroughColor { + [self yy_setStrikethroughColor:strikethroughColor range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_underlineStyle:(NSUnderlineStyle)underlineStyle { + [self yy_setUnderlineStyle:underlineStyle range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_underlineColor:(UIColor *)underlineColor { + [self yy_setUnderlineColor:underlineColor range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_ligature:(NSNumber *)ligature { + [self yy_setLigature:ligature range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_textEffect:(NSString *)textEffect { + [self yy_setTextEffect:textEffect range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_obliqueness:(NSNumber *)obliqueness { + [self yy_setObliqueness:obliqueness range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_expansion:(NSNumber *)expansion { + [self yy_setExpansion:expansion range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_baselineOffset:(NSNumber *)baselineOffset { + [self yy_setBaselineOffset:baselineOffset range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_verticalGlyphForm:(BOOL)verticalGlyphForm { + [self yy_setVerticalGlyphForm:verticalGlyphForm range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_language:(NSString *)language { + [self yy_setLanguage:language range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_writingDirection:(NSArray *)writingDirection { + [self yy_setWritingDirection:writingDirection range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_paragraphStyle:(NSParagraphStyle *)paragraphStyle { + /* + NSParagraphStyle is NOT toll-free bridged to CTParagraphStyleRef. + + CoreText can use both NSParagraphStyle and CTParagraphStyleRef, + but UILabel/UITextView can only use NSParagraphStyle. + + We use NSParagraphStyle in both CoreText and UIKit. + */ + [self yy_setParagraphStyle:paragraphStyle range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_alignment:(NSTextAlignment)alignment { + [self yy_setAlignment:alignment range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_baseWritingDirection:(NSWritingDirection)baseWritingDirection { + [self yy_setBaseWritingDirection:baseWritingDirection range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_lineSpacing:(CGFloat)lineSpacing { + [self yy_setLineSpacing:lineSpacing range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_paragraphSpacing:(CGFloat)paragraphSpacing { + [self yy_setParagraphSpacing:paragraphSpacing range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_paragraphSpacingBefore:(CGFloat)paragraphSpacingBefore { + [self yy_setParagraphSpacing:paragraphSpacingBefore range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_firstLineHeadIndent:(CGFloat)firstLineHeadIndent { + [self yy_setFirstLineHeadIndent:firstLineHeadIndent range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_headIndent:(CGFloat)headIndent { + [self yy_setHeadIndent:headIndent range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_tailIndent:(CGFloat)tailIndent { + [self yy_setTailIndent:tailIndent range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_lineBreakMode:(NSLineBreakMode)lineBreakMode { + [self yy_setLineBreakMode:lineBreakMode range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_minimumLineHeight:(CGFloat)minimumLineHeight { + [self yy_setMinimumLineHeight:minimumLineHeight range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_maximumLineHeight:(CGFloat)maximumLineHeight { + [self yy_setMaximumLineHeight:maximumLineHeight range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_lineHeightMultiple:(CGFloat)lineHeightMultiple { + [self yy_setLineHeightMultiple:lineHeightMultiple range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_hyphenationFactor:(float)hyphenationFactor { + [self yy_setHyphenationFactor:hyphenationFactor range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_defaultTabInterval:(CGFloat)defaultTabInterval { + [self yy_setDefaultTabInterval:defaultTabInterval range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_tabStops:(NSArray *)tabStops { + [self yy_setTabStops:tabStops range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_textShadow:(YYTextShadow *)textShadow { + [self yy_setTextShadow:textShadow range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_textInnerShadow:(YYTextShadow *)textInnerShadow { + [self yy_setTextInnerShadow:textInnerShadow range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_textUnderline:(YYTextDecoration *)textUnderline { + [self yy_setTextUnderline:textUnderline range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_textStrikethrough:(YYTextDecoration *)textStrikethrough { + [self yy_setTextStrikethrough:textStrikethrough range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_textBorder:(YYTextBorder *)textBorder { + [self yy_setTextBorder:textBorder range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_textBackgroundBorder:(YYTextBorder *)textBackgroundBorder { + [self yy_setTextBackgroundBorder:textBackgroundBorder range:NSMakeRange(0, self.length)]; +} + +- (void)setYy_textGlyphTransform:(CGAffineTransform)textGlyphTransform { + [self yy_setTextGlyphTransform:textGlyphTransform range:NSMakeRange(0, self.length)]; +} + +#pragma mark - Range Setter + +- (void)yy_setFont:(UIFont *)font range:(NSRange)range { + /* + In iOS7 and later, UIFont is toll-free bridged to CTFontRef, + although Apple does not mention it in documentation. + + In iOS6, UIFont is a wrapper for CTFontRef, so CoreText can alse use UIfont, + but UILabel/UITextView cannot use CTFontRef. + + We use UIFont for both CoreText and UIKit. + */ + [self yy_setAttribute:NSFontAttributeName value:font range:range]; +} + +- (void)yy_setKern:(NSNumber *)kern range:(NSRange)range { + [self yy_setAttribute:NSKernAttributeName value:kern range:range]; +} + +- (void)yy_setColor:(UIColor *)color range:(NSRange)range { + [self yy_setAttribute:(id)kCTForegroundColorAttributeName value:(id)color.CGColor range:range]; + [self yy_setAttribute:NSForegroundColorAttributeName value:color range:range]; +} + +- (void)yy_setBackgroundColor:(UIColor *)backgroundColor range:(NSRange)range { + [self yy_setAttribute:NSBackgroundColorAttributeName value:backgroundColor range:range]; +} + +- (void)yy_setStrokeWidth:(NSNumber *)strokeWidth range:(NSRange)range { + [self yy_setAttribute:NSStrokeWidthAttributeName value:strokeWidth range:range]; +} + +- (void)yy_setStrokeColor:(UIColor *)strokeColor range:(NSRange)range { + [self yy_setAttribute:(id)kCTStrokeColorAttributeName value:(id)strokeColor.CGColor range:range]; + [self yy_setAttribute:NSStrokeColorAttributeName value:strokeColor range:range]; +} + +- (void)yy_setShadow:(NSShadow *)shadow range:(NSRange)range { + [self yy_setAttribute:NSShadowAttributeName value:shadow range:range]; +} + +- (void)yy_setStrikethroughStyle:(NSUnderlineStyle)strikethroughStyle range:(NSRange)range { + NSNumber *style = strikethroughStyle == 0 ? nil : @(strikethroughStyle); + [self yy_setAttribute:NSStrikethroughStyleAttributeName value:style range:range]; +} + +- (void)yy_setStrikethroughColor:(UIColor *)strikethroughColor range:(NSRange)range { + if (kSystemVersion >= 7) { + [self yy_setAttribute:NSStrikethroughColorAttributeName value:strikethroughColor range:range]; + } +} + +- (void)yy_setUnderlineStyle:(NSUnderlineStyle)underlineStyle range:(NSRange)range { + NSNumber *style = underlineStyle == 0 ? nil : @(underlineStyle); + [self yy_setAttribute:NSUnderlineStyleAttributeName value:style range:range]; +} + +- (void)yy_setUnderlineColor:(UIColor *)underlineColor range:(NSRange)range { + [self yy_setAttribute:(id)kCTUnderlineColorAttributeName value:(id)underlineColor.CGColor range:range]; + if (kSystemVersion >= 7) { + [self yy_setAttribute:NSUnderlineColorAttributeName value:underlineColor range:range]; + } +} + +- (void)yy_setLigature:(NSNumber *)ligature range:(NSRange)range { + [self yy_setAttribute:NSLigatureAttributeName value:ligature range:range]; +} + +- (void)yy_setTextEffect:(NSString *)textEffect range:(NSRange)range { + if (kSystemVersion >= 7) { + [self yy_setAttribute:NSTextEffectAttributeName value:textEffect range:range]; + } +} + +- (void)yy_setObliqueness:(NSNumber *)obliqueness range:(NSRange)range { + if (kSystemVersion >= 7) { + [self yy_setAttribute:NSObliquenessAttributeName value:obliqueness range:range]; + } +} + +- (void)yy_setExpansion:(NSNumber *)expansion range:(NSRange)range { + if (kSystemVersion >= 7) { + [self yy_setAttribute:NSExpansionAttributeName value:expansion range:range]; + } +} + +- (void)yy_setBaselineOffset:(NSNumber *)baselineOffset range:(NSRange)range { + if (kSystemVersion >= 7) { + [self yy_setAttribute:NSBaselineOffsetAttributeName value:baselineOffset range:range]; + } +} + +- (void)yy_setVerticalGlyphForm:(BOOL)verticalGlyphForm range:(NSRange)range { + NSNumber *v = verticalGlyphForm ? @(YES) : nil; + [self yy_setAttribute:NSVerticalGlyphFormAttributeName value:v range:range]; +} + +- (void)yy_setLanguage:(NSString *)language range:(NSRange)range { + if (kSystemVersion >= 7) { + [self yy_setAttribute:(id)kCTLanguageAttributeName value:language range:range]; + } +} + +- (void)yy_setWritingDirection:(NSArray *)writingDirection range:(NSRange)range { + [self yy_setAttribute:(id)kCTWritingDirectionAttributeName value:writingDirection range:range]; +} + +- (void)yy_setParagraphStyle:(NSParagraphStyle *)paragraphStyle range:(NSRange)range { + /* + NSParagraphStyle is NOT toll-free bridged to CTParagraphStyleRef. + + CoreText can use both NSParagraphStyle and CTParagraphStyleRef, + but UILabel/UITextView can only use NSParagraphStyle. + + We use NSParagraphStyle in both CoreText and UIKit. + */ + [self yy_setAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range]; +} + +#define ParagraphStyleSet(_attr_) \ +[self enumerateAttribute:NSParagraphStyleAttributeName \ + inRange:range \ + options:kNilOptions \ + usingBlock: ^(NSParagraphStyle *value, NSRange subRange, BOOL *stop) { \ + NSMutableParagraphStyle *style = nil; \ + if (value) { \ + if (CFGetTypeID((__bridge CFTypeRef)(value)) == CTParagraphStyleGetTypeID()) { \ + value = [NSParagraphStyle yy_styleWithCTStyle:(__bridge CTParagraphStyleRef)(value)]; \ + } \ + if (value. _attr_ == _attr_) return; \ + if ([value isKindOfClass:[NSMutableParagraphStyle class]]) { \ + style = (id)value; \ + } else { \ + style = value.mutableCopy; \ + } \ + } else { \ + if ([NSParagraphStyle defaultParagraphStyle]. _attr_ == _attr_) return; \ + style = [NSParagraphStyle defaultParagraphStyle].mutableCopy; \ + } \ + style. _attr_ = _attr_; \ + [self yy_setParagraphStyle:style range:subRange]; \ + }]; + +- (void)yy_setAlignment:(NSTextAlignment)alignment range:(NSRange)range { + ParagraphStyleSet(alignment); +} + +- (void)yy_setBaseWritingDirection:(NSWritingDirection)baseWritingDirection range:(NSRange)range { + ParagraphStyleSet(baseWritingDirection); +} + +- (void)yy_setLineSpacing:(CGFloat)lineSpacing range:(NSRange)range { + ParagraphStyleSet(lineSpacing); +} + +- (void)yy_setParagraphSpacing:(CGFloat)paragraphSpacing range:(NSRange)range { + ParagraphStyleSet(paragraphSpacing); +} + +- (void)yy_setParagraphSpacingBefore:(CGFloat)paragraphSpacingBefore range:(NSRange)range { + ParagraphStyleSet(paragraphSpacingBefore); +} + +- (void)yy_setFirstLineHeadIndent:(CGFloat)firstLineHeadIndent range:(NSRange)range { + ParagraphStyleSet(firstLineHeadIndent); +} + +- (void)yy_setHeadIndent:(CGFloat)headIndent range:(NSRange)range { + ParagraphStyleSet(headIndent); +} + +- (void)yy_setTailIndent:(CGFloat)tailIndent range:(NSRange)range { + ParagraphStyleSet(tailIndent); +} + +- (void)yy_setLineBreakMode:(NSLineBreakMode)lineBreakMode range:(NSRange)range { + ParagraphStyleSet(lineBreakMode); +} + +- (void)yy_setMinimumLineHeight:(CGFloat)minimumLineHeight range:(NSRange)range { + ParagraphStyleSet(minimumLineHeight); +} + +- (void)yy_setMaximumLineHeight:(CGFloat)maximumLineHeight range:(NSRange)range { + ParagraphStyleSet(maximumLineHeight); +} + +- (void)yy_setLineHeightMultiple:(CGFloat)lineHeightMultiple range:(NSRange)range { + ParagraphStyleSet(lineHeightMultiple); +} + +- (void)yy_setHyphenationFactor:(float)hyphenationFactor range:(NSRange)range { + ParagraphStyleSet(hyphenationFactor); +} + +- (void)yy_setDefaultTabInterval:(CGFloat)defaultTabInterval range:(NSRange)range { + if (!kiOS7Later) return; + ParagraphStyleSet(defaultTabInterval); +} + +- (void)yy_setTabStops:(NSArray *)tabStops range:(NSRange)range { + if (!kiOS7Later) return; + ParagraphStyleSet(tabStops); +} + +#undef ParagraphStyleSet + +- (void)yy_setSuperscript:(NSNumber *)superscript range:(NSRange)range { + if ([superscript isEqualToNumber:@(0)]) { + superscript = nil; + } + [self yy_setAttribute:(id)kCTSuperscriptAttributeName value:superscript range:range]; +} + +- (void)yy_setGlyphInfo:(CTGlyphInfoRef)glyphInfo range:(NSRange)range { + [self yy_setAttribute:(id)kCTGlyphInfoAttributeName value:(__bridge id)glyphInfo range:range]; +} + +- (void)yy_setCharacterShape:(NSNumber *)characterShape range:(NSRange)range { + [self yy_setAttribute:(id)kCTCharacterShapeAttributeName value:characterShape range:range]; +} + +- (void)yy_setRunDelegate:(CTRunDelegateRef)runDelegate range:(NSRange)range { + [self yy_setAttribute:(id)kCTRunDelegateAttributeName value:(__bridge id)runDelegate range:range]; +} + +- (void)yy_setBaselineClass:(CFStringRef)baselineClass range:(NSRange)range { + [self yy_setAttribute:(id)kCTBaselineClassAttributeName value:(__bridge id)baselineClass range:range]; +} + +- (void)yy_setBaselineInfo:(CFDictionaryRef)baselineInfo range:(NSRange)range { + [self yy_setAttribute:(id)kCTBaselineInfoAttributeName value:(__bridge id)baselineInfo range:range]; +} + +- (void)yy_setBaselineReferenceInfo:(CFDictionaryRef)referenceInfo range:(NSRange)range { + [self yy_setAttribute:(id)kCTBaselineReferenceInfoAttributeName value:(__bridge id)referenceInfo range:range]; +} + +- (void)yy_setRubyAnnotation:(CTRubyAnnotationRef)ruby range:(NSRange)range { + if (kSystemVersion >= 8) { + [self yy_setAttribute:(id)kCTRubyAnnotationAttributeName value:(__bridge id)ruby range:range]; + } +} + +- (void)yy_setAttachment:(NSTextAttachment *)attachment range:(NSRange)range { + if (kSystemVersion >= 7) { + [self yy_setAttribute:NSAttachmentAttributeName value:attachment range:range]; + } +} + +- (void)yy_setLink:(id)link range:(NSRange)range { + if (kSystemVersion >= 7) { + [self yy_setAttribute:NSLinkAttributeName value:link range:range]; + } +} + +- (void)yy_setTextBackedString:(YYTextBackedString *)textBackedString range:(NSRange)range { + [self yy_setAttribute:YYTextBackedStringAttributeName value:textBackedString range:range]; +} + +- (void)yy_setTextBinding:(YYTextBinding *)textBinding range:(NSRange)range { + [self yy_setAttribute:YYTextBindingAttributeName value:textBinding range:range]; +} + +- (void)yy_setTextShadow:(YYTextShadow *)textShadow range:(NSRange)range { + [self yy_setAttribute:YYTextShadowAttributeName value:textShadow range:range]; +} + +- (void)yy_setTextInnerShadow:(YYTextShadow *)textInnerShadow range:(NSRange)range { + [self yy_setAttribute:YYTextInnerShadowAttributeName value:textInnerShadow range:range]; +} + +- (void)yy_setTextUnderline:(YYTextDecoration *)textUnderline range:(NSRange)range { + [self yy_setAttribute:YYTextUnderlineAttributeName value:textUnderline range:range]; +} + +- (void)yy_setTextStrikethrough:(YYTextDecoration *)textStrikethrough range:(NSRange)range { + [self yy_setAttribute:YYTextStrikethroughAttributeName value:textStrikethrough range:range]; +} + +- (void)yy_setTextBorder:(YYTextBorder *)textBorder range:(NSRange)range { + [self yy_setAttribute:YYTextBorderAttributeName value:textBorder range:range]; +} + +- (void)yy_setTextBackgroundBorder:(YYTextBorder *)textBackgroundBorder range:(NSRange)range { + [self yy_setAttribute:YYTextBackgroundBorderAttributeName value:textBackgroundBorder range:range]; +} + +- (void)yy_setTextAttachment:(YYTextAttachment *)textAttachment range:(NSRange)range { + [self yy_setAttribute:YYTextAttachmentAttributeName value:textAttachment range:range]; +} + +- (void)yy_setTextHighlight:(YYTextHighlight *)textHighlight range:(NSRange)range { + [self yy_setAttribute:YYTextHighlightAttributeName value:textHighlight range:range]; +} + +- (void)yy_setTextBlockBorder:(YYTextBorder *)textBlockBorder range:(NSRange)range { + [self yy_setAttribute:YYTextBlockBorderAttributeName value:textBlockBorder range:range]; +} + +- (void)yy_setTextRubyAnnotation:(YYTextRubyAnnotation *)ruby range:(NSRange)range { + if (kiOS8Later) { + CTRubyAnnotationRef rubyRef = [ruby CTRubyAnnotation]; + [self yy_setRubyAnnotation:rubyRef range:range]; + if (rubyRef) CFRelease(rubyRef); + } +} + +- (void)yy_setTextGlyphTransform:(CGAffineTransform)textGlyphTransform range:(NSRange)range { + NSValue *value = CGAffineTransformIsIdentity(textGlyphTransform) ? nil : [NSValue valueWithCGAffineTransform:textGlyphTransform]; + [self yy_setAttribute:YYTextGlyphTransformAttributeName value:value range:range]; +} + +- (void)yy_setTextHighlightRange:(NSRange)range + color:(UIColor *)color + backgroundColor:(UIColor *)backgroundColor + userInfo:(NSDictionary *)userInfo + tapAction:(YYTextAction)tapAction + longPressAction:(YYTextAction)longPressAction { + YYTextHighlight *highlight = [YYTextHighlight highlightWithBackgroundColor:backgroundColor]; + highlight.userInfo = userInfo; + highlight.tapAction = tapAction; + highlight.longPressAction = longPressAction; + if (color) [self yy_setColor:color range:range]; + [self yy_setTextHighlight:highlight range:range]; +} + +- (void)yy_setTextHighlightRange:(NSRange)range + color:(UIColor *)color + backgroundColor:(UIColor *)backgroundColor + tapAction:(YYTextAction)tapAction { + [self yy_setTextHighlightRange:range + color:color + backgroundColor:backgroundColor + userInfo:nil + tapAction:tapAction + longPressAction:nil]; +} + +- (void)yy_setTextHighlightRange:(NSRange)range + color:(UIColor *)color + backgroundColor:(UIColor *)backgroundColor + userInfo:(NSDictionary *)userInfo { + [self yy_setTextHighlightRange:range + color:color + backgroundColor:backgroundColor + userInfo:userInfo + tapAction:nil + longPressAction:nil]; +} + +- (void)yy_insertString:(NSString *)string atIndex:(NSUInteger)location { + [self replaceCharactersInRange:NSMakeRange(location, 0) withString:string]; + [self yy_removeDiscontinuousAttributesInRange:NSMakeRange(location, string.length)]; +} + +- (void)yy_appendString:(NSString *)string { + NSUInteger length = self.length; + [self replaceCharactersInRange:NSMakeRange(length, 0) withString:string]; + [self yy_removeDiscontinuousAttributesInRange:NSMakeRange(length, string.length)]; +} + +- (void)yy_setClearColorToJoinedEmoji { + NSString *str = self.string; + if (str.length < 8) return; + + // Most string do not contains the joined-emoji, test the joiner first. + BOOL containsJoiner = NO; + { + CFStringRef cfStr = (__bridge CFStringRef)str; + BOOL needFree = NO; + UniChar *chars = NULL; + chars = (void *)CFStringGetCharactersPtr(cfStr); + if (!chars) { + chars = malloc(str.length * sizeof(UniChar)); + if (chars) { + needFree = YES; + CFStringGetCharacters(cfStr, CFRangeMake(0, str.length), chars); + } + } + if (!chars) { // fail to get unichar.. + containsJoiner = YES; + } else { + for (int i = 0, max = (int)str.length; i < max; i++) { + if (chars[i] == 0x200D) { // 'ZERO WIDTH JOINER' (U+200D) + containsJoiner = YES; + break; + } + } + if (needFree) free(chars); + } + } + if (!containsJoiner) return; + + // NSRegularExpression is designed to be immutable and thread safe. + static NSRegularExpression *regex; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + regex = [NSRegularExpression regularExpressionWithPattern:@"((�������������������������|�������������������������|�������������������������|�������������������������|�������������������������|�������������������������|�������������������������|�������������������������|�������������������������)+|(������������������|������������������|������������������|������������������|������������������))" options:kNilOptions error:nil]; + }); + + UIColor *clear = [UIColor clearColor]; + [regex enumerateMatchesInString:str options:kNilOptions range:NSMakeRange(0, str.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { + [self yy_setColor:clear range:result.range]; + }]; +} + +- (void)yy_removeDiscontinuousAttributesInRange:(NSRange)range { + NSArray *keys = [NSMutableAttributedString yy_allDiscontinuousAttributeKeys]; + for (NSString *key in keys) { + [self removeAttribute:key range:range]; + } +} + ++ (NSArray *)yy_allDiscontinuousAttributeKeys { + static NSMutableArray *keys; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + keys = @[(id)kCTSuperscriptAttributeName, + (id)kCTRunDelegateAttributeName, + YYTextBackedStringAttributeName, + YYTextBindingAttributeName, + YYTextAttachmentAttributeName].mutableCopy; + if (kiOS8Later) { + [keys addObject:(id)kCTRubyAnnotationAttributeName]; + } + if (kiOS7Later) { + [keys addObject:NSAttachmentAttributeName]; + } + }); + return keys; +} + +@end -- Gitblit v1.8.0