2023年7月14日发(作者:)
【⽆限互联】iOS开发之图⽂混排研究【⽆限互联】ios开发之图⽂混排研究本节要点:1.理解图⽂混排的概念。2.研究FTCoreText框架的实现原理。3.演⽰使⽤FTCoreText框架实现图⽂混排的demo,以及效果图。图⽂混排的概念● 在view上显⽰的数据既包括⽂本数据,也包括图⽚数据。● 图⽚的⼤⼩可以不同,图⽚和⽂字的排列顺序不固定。● ⽂本数据的字体、颜⾊、背景等其他属性可以不同。● ⽂本数据中还可以包含超链接。可以点击超链接进⼊其他页⾯。研究FTCoreText框架的实现原理1>.此框架的功能和缺陷1.1> 功能:此框架能够实现不同风格的⽂本数据、图⽚数据的混合显⽰,还可以设置超链接。1.2> 缺陷:此框架⾥⾯的⽂本的处理只针对与XML或者HTML格式的数据。(⽐如这种标签类型的数据:)对于JSON格式的数据,可能需要⾃⼰修改内部⼀些⽅法的实现。2>.此框架的基本构成2.1> 此框架有两个类组成,分别是FTCoreTextStyle类、FTCoreTextView类。2.2> FTCoreTextStyle类主要是⽤于设置格式的,定义了⼀些属性和⽅法。通过这个类就可以定义⼀种格式。2.3> FTCoreTextView这个类是此框架的核⼼类。这个类⾥⾯定义了⼀个私有的类FTCoreTextNode。需要处理的数据中,每⼀个标签()对应⼀个节点。节点类⾥⾯就封装了⼀些节点的属性和⽅法,供FTCoreTextView这个类使⽤。2.4> FTCoreTextView这个类主要封装了对数据的处理、绘制图⽚和⽂本,其中包含了很多处理细节的⽅法。3>.FTCoreText框架内部具体的实现原理3.1>FTCoreTextStyle类的主要实现FTCoreTextStyle类封装的⼀些重要的属性:[objc]view plaincopyprint?1.@property (nonatomic) NSString *name; //格式的名称2.@property (nonatomic) NSString *appendedCharacter; //追加的字符串3.@property (nonatomic) UIFont *font; //字体4.@property (nonatomic) UIColor *color; //颜⾊5.@property (nonatomic, getter=isUnderLined) BOOL underlined; //下划线6.@property (nonatomic) FTCoreTextAlignement textAlignment; //⽂字显⽰位置7.@property (nonatomic) UIEdgeInsets paragraphInset; //段落之间的间隔8.@property (nonatomic) CGFloat leading;9.@property (nonatomic) CGFloat maxLineHeight; //最⼤⾏⾼10.@property (nonatomic) CGFloat minLineHeight; //最⼩⾏⾼11.// bullet 格式12.@property (nonatomic) NSString *bulletCharacter; //设置bullet字符13.@property (nonatomic) UIFont *bulletFont; // 设置bullet字体14.@property (nonatomic) UIColor *bulletColor; //设置bullet颜⾊15.//当格式被解析时,回调的block16.@property (nonatomic, copy) FTCoreTextCallbackBlock block;17.@property (nonatomic, assign) BOOL applyParagraphStyling; //段落格式FTCoreTextStyle类封装的⼀些重要的⽅法:[objc]view plaincopyprint?1.//初始化⽅法2.- (id)init3.{ = [super init]; (self) {/doc/
= @"_default";Character = @"?";edCharacter = @""; = [UIFont systemFontOfSize:12]; = [UIColor blackColor];ined = NO;ignment = FTCoreTextAlignementLeft;eHeight = 0;eHeight = 0;aphInset = UIEdgeInsetsZero;aragraphStyling = YES;g = 0;=nil;19.} self;21.}[objc]view plaincopyprint?1.//类⽅法,通过name来定义⼀种格式2.+ (id)styleWithName:(NSString *)name3.{TextStyle *style = [[FTCoreTextStyle alloc] init];5.[style setName:name]; style;7.}3.2>FTCoreTextNode的主要实现FTCoreTextNode封装的⼀些主要的属性:[objc]view plaincopyprint?1.@property (nonatomic, assign) FTCoreTextNode*supernode; //⽗节点2.@property (nonatomic) NSArray *subnodes; //所有⼦类节点3.@property (nonatomic, copy) FTCoreTextStyle *style; //定义⼀种格式4.@property (nonatomic) NSRange styleRange; //定义格式应⽤的范围5.@property (nonatomic) BOOL isClosed; //是否是关闭6.@property (nonatomic) NSInteger startLocation; //开始的位置7.@property (nonatomic) BOOL isLink; //是否是链接8.@property (nonatomic) BOOL isImage; //是否是图⽚9.@property (nonatomic) BOOL isBullet; //是否是Bullet10.@property (nonatomic) NSString *imageName; //图⽚的名称FTCoreTextNode封装的⼀些主要的⽅法:[objc]view plaincopyprint?1.//在所有⼦节点的最后插⼊⼀个⼦节点2.- (void)addSubnode:(FTCoreTextNode *)node3.{4.[self insertSubnode:node atIndex:[_subnodes count]];5.}6.7.//在指定的位置插⼊⼦节点8.- (void)insertSubnode:(FTCoreTextNode *)subnode atIndex:(NSUInteger)index9.{10.//设置⽗节点ode = self;bleArray *subnodes = (NSMutableArray *)es;13.//判断index是否超出数组_subnodes的长度 (index <= [_subnodes count]) {15.[subnodes insertObject:subnode atIndex:index];16.} {18.[subnodes addObject:subnode];19.}20.}1.//获取节点的索引2.- (NSUInteger)nodeIndex3.{ [_es indexOfObject:self];5.}6.7.//获取指定位置的⼦节点8.- (FTCoreTextNode *)subnodeAtIndex:(NSUInteger)index9.{ (index < [_subnodes count]) { [_subnodes objectAtIndex:index];12.} nil;14.}15.3.3>FTCoreTextView类的主要实现FTCoreTextView封装的⼀些主要的属性:[objc]view plaincopyprint?1.@property (nonatomic) NSString *text; //⽂本内容2.@property (nonatomic) NSString *processedString; //处理之后的⽂本3.@property (nonatomic, readonly) NSAttributedString *attributedString; //属性⽂本4.@property (nonatomic, assign) CGPathRef path; //路径5.@property (nonatomic) NSMutableDictionary *URLs; //存储所有的URL6.@property (nonatomic) NSMutableArray *images; //存储所有图⽚7.@property (nonatomic, assign) id delegate; //设置代理对象8.@property (nonatomic) UIColor *shadowColor; //阴影颜⾊9.@property (nonatomic) CGSize shadowOffset; //阴影⼤⼩10.@property (nonatomic) BOOL verbose;11.@property (nonatomic) BOOL highlightT ouch; //触摸时⾼亮状态1.@property(nonatomic) CTFramesetterRef framesetter;2.@property (nonatomic) FTCoreTextNode *rootNode; //节点3.@property (nonatomic, readwrite) NSAttributedString *attributedString; //属性⽂本4.@property (nonatomic) NSDictionary *touchedData; //点击的⽂本数据的属性的key和value的映射5.@property (nonatomic) NSArray *selectionsViews; //选中的视图 [objc]view plaincopyprint?1.//以下常量是⼀些默认的标记(标签)名称 NSString * const FTCoreTextT agDefault; //默认标记 NSString * const FTCoreTextT agImage; //图⽚标记 NSString * const FTCoreTextT agBullet; //Bullet标记 NSString * const FTCoreTextT agPage; //< _page / >分页标记 NSString * const FTCoreTextT agLink; //超链接标记 NSString * const FTCoreTextT agParagraph; //段落标记 [objc]view plaincopyprint?1.//以下这些常量被使⽤在字典中的属性,这个字典是作为代理对象的协议⽅法的参数 NSString * const FTCoreTextDataURL; //定义 NSString * const FTCoreTextDataName; //定义 NSString * const FTCoreTextDataFrame; //定义 NSString * const FTCoreTextDataAttributes; //定义Attributes6.@protocol FTCoreTextViewDelegate; //定义协议FTCoreTextView封装的⼀些主要的⽅法(主要⽅法的调⽤流程及实现的功能):[objc]view plaincopyprint?1./*2.调整视图的⾼度:3.1、创建⼀个CGSize宽度就为视图当前的宽度,⾼度⽆穷⼤4.2、调⽤将此suggestedSizeConstrainedToSize⽅法,CGSize作为参数。在这个⽅法中通过_framesetter和CGSize计算出⼀个合适的size,并返回size。由此确定当前视图的frame.5.*/6.- (void)fitToSuggestedHeight7.{ suggestedSize = [self suggestedSizeConstrainedToSize:CGSizeMake(CGRectGetWidth( me), MAXFLOAT)]; viewFrame = ; = ; = viewFrame;12.}[objc]view plaincopyprint?1.//计算⽂本的CGSize2.- (CGSize)suggestedSizeConstrainedT oSize:(CGSize)size3.{ suggestedSize;5.[self updateFramesetterIfNeeded]; (_framesetter == NULL) { CGSizeZero;8.}tedSize = CTFramesetterSuggestFrameSizeWithConstraints(_framesetter,CFRangeMake(0, 0), NULL, size, NULL);tedSize = CGSizeMake(ceilf(),ceilf()); suggestedSize;12.}[objc]view plaincopyprint?1.//更新Framesetter2.- (void)updateFramesetterIfNeeded3.{ (!_dAttrString) { (_framesetter != NULL) CFRelease(_framesetter);6._framesetter = CTFramesetterCreateWithAttributedString((__bridgeCFAttributedStringRef)utedString);7._coreT dAttrString = YES;8._coreT dFramesetter = YES;9.}10.}[objc]view plaincopyprint?1.//取得处理之后的属性⽂本2.- (NSAttributedString *)attributedString3.{ (!_dAttrString) {5._coreT dAttrString = YES; (_processedString == nil || _angesMade|| !_coreT dFramesetter) {8._coreT angesMade = NO;9.[self processT ext];10.} (_processedString) {bleAttributedString *string = [[NSMutableAttributedString alloc]initWithString:_processedString]; (FTCoreTextNode *node in [_rootNode allSubnodes]) {17.[self applyStyle: inRange:angeonString:&string];18.}19.20._attributedString = string;21.}22.} _attributedString;24.}[objc]view plaincopyprint?1./*2.处理⽂本的⽅法的实现(⽐较核⼼的⽅法):3.1、定义正则表达式,在要处理的字符串中查找出符合要求的字符串。ng *regEx = @"<(/){0,1}.*?( /){0,1}>"; 此正则表达式,可以查找出三类标签,例如:
5.2、以上三类标签分别对应代码中的3中类型:6.1>. 对应这种类型tagType = FTCoreTextTagTypeOpen;7.2>. 对应这种类型tagType = FTCoreTextTagTypeClose;8.3>. 对应这种类型tagType = FTCoreTextTagTypeSelfClose;9.3、获取到标签后,取得标签的名称。在对应的标签类型下进⾏判断和处理。10.4、每个这样的标签(123)对应⼀个节点。每次取得⼀个标签的头部(标签类型:FTCoreTextT agTypeOpen),就创建⼀个节点。通过标签的名称判断是否是超链接、图⽚等等。11.判断完之后,就设置当前节点的属性(节点有很多属性:isLink,isImage等等)。如果是图⽚,则设置e = YES; 进⾏下次循环时,取得标签尾部(标签类型:FTCoreT extTagTypeClose),就直接通过当前节点的属性判断是否是图⽚标签,或者其他类型。根据判断结果,使⽤封装好的FTCoreT extStyle类,为不同的标签内容设置不同的格式Style为标签的内容设置不同的格式。12.*/13.- (void)processT ext14.{ (!_text || [_text length] == 0) return;16.[_URLs removeAllObjects];17.[_images removeAllObjects];TextNode *rootNode = [FTCoreT extNode new]; = [_styles objectForKey:[selfdefaultTagNameForKey:FTCoreTextTagDefault]];TextNode *currentSupernode = rootNode;bleString *processedString = [NSMutableString stringWithString:_text]; finished = NO;e remainingRange = NSMakeRange(0, [processedString length]);24.//定义了正则表达式,查找标签,例如:
ng *regEx =@"<(/){0,1}.*?( /){0,1}>"; (!finished) {e tagRange = [processedString rangeOfString:regExoptions:NSRegularExpressionSearch range:remainingRange]; (on == NSNotFound) { (currentSupernode != rootNode && !ed) { (_verbose) NSLog(@"FTCoreTextView :%@ - Couldn't parse text because tag'%@' at position %d is not closed - aborting rendering", self,/doc/
, ocation);;35.}ed = YES;ue;38.}ng *fullT ag = [processedString substringWithRange:tagRange];TextT agType tagType; ([fullT ag rangeOfString:@"43.//标签e = FTCoreTextT agTypeClose;45.} if ([fullTag rangeOfString:@"/>"].location == NSNotFound && [fullT agrangeOfString:@" />"].location == NSNotFound) {47.//标签或者或者e = FTCoreTextT agTypeOpen;49.} {51.//标签e = FTCoreTextT agTypeSelfClose;53.}54.//获取整个标签所有组成部分y *tagsComponents = [fullTag componentsSeparatedByString:@" "];56.57.//获取标签名称(⾥⾯可能包括"<")ng *tagName = ( > 0) ? [tagsComponentsobjectAtIndex:0] : fullTag;59.60.//这⾥获得的tagName的值例如:_e = [tagName stringByTrimmingCharactersInSet:[NSCharacterSetcharacterSetWithCharactersInString:@"< />"]];TextStyle *style = [_styles objectForKey:tagName]; (style == nil) { = [_styles objectForKey:[selfdefaultTagNameForKey:FTCoreTextTagDefault]]; (_verbose) NSLog(@"FTCoreTextView :%@ - Couldn't find style for tag '%@'",self, tagName);68.}69.//判断标签的类型 (tagType) { FTCoreTextT agTypeOpen:72.{ ( || e) {ng *predefinedT ag = nil; () predefinedT ag = [selfdefaultTagNameForKey:FTCoreTextTagLink]; if (e) predefinedTag = [selfdefaultTagNameForKey:FTCoreTextTagImage]; (_verbose) NSLog(@"FTCoreTextView :%@ - You can't open a new tag inside a'%@' tag - aborting rendering", self, predefinedTag);;79.}TextNode *newNode = [FTCoreTextNode new]; = style;ocation = on;83.//判断是否是链接 ([tagName isEqualToString:[self defaultTagNameForKey:FTCoreTextT agLink]]) { = YES;86.}87.//判断是否是 if ([tagName isEqualToString:[selfdefaultTagNameForKey:FTCoreTextTagBullet]]) {et = YES;ng *appendedString = [NSString stringWithFormat:@"%@t",Character];91.[processedString insertString:appendedString atIndex:on +];92.93.//bullet styling94.//设置⼦标签的样式TextStyle *bulletStyle = [FTCoreT extStyle new];/doc/
= @"_FTBulletStyle"; = Font; = Color;aragraphStyling = NO;aphInset = UIEdgeInsetsMake(0, 0, 0,);TextNode *bulletNode = [FTCoreT extNode new]; = bulletStyle;ange = NSMakeRange(on, [appendedString length]);105.106.[newNode addSubnode:bulletNode];107.}108.//判断是否是图⽚ if ([tagName isEqualToString:[selfdefaultTagNameForKey:FTCoreTextTagImage]]) {e = YES;111.}112.[processedString replaceCharactersInRange:tagRange withString:@""]; 113.[currentSupernode addSubnode:newNode];tSupernode = newNode;on = on; = [processedString length] - on; 117.}; FTCoreTextT agTypeClose:120.{ ((![/doc/
isEqualToString:[selfdefaultTagNameForKey:FTCoreTextTagDefault]]&& ![/doc/
isEqualToString:tagName]) ) { (_verbose) NSLog(@"FTCoreTextView :%@ - Closing tag '%@' at range %@ doesn't match open tag '%@' - aborting rendering", self, fullT ag,NSStringFromRange(tagRange), /doc/
);;124.}ed = YES; () {e elementContentRange =NSMakeRange(ocation, on -ocation);ng *elementContent = [processedStringsubstringWithRange:elementContentRange];e pipeRange = [elementContent rangeOfString:@"|"];ng *urlString = nil;ng *urlDescription = nil; (on != NSNotFound) {ing = [elementContent substringToIndex:on] ;cription = [elementContent substringFromIndex:on + 1]; 136.}137.[processedStringreplaceCharactersInRange:NSMakeRange(on, + ) withString:urlDescription]; (!([[urlString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlinestringByTrimmingCharactersInSet:[NSCharacterSetwhitespaceAndNewlineCharacterSet]] hasPrefix:@""]))139.{ing = [NSString stringWithFormat:@"%@", urlString];141.} *url = [NSURL URLWithString:[urlStringstringByTrimmingCharactersInSet:[NSCharacterSetwhitespaceAndNewlineCharacterSet]]];e urlDescriptionRange =NSMakeRange(on, [urlDescription length]);144.[_URLs setObject:url forKey:NSStringFromRange(urlDescriptionRange)]; ange = urlDescriptionRange;147.} if (e) {e elementContentRange =NSMakeRange(ocation, on -ocation);ng *elementContent = [processedStringsubstringWithRange:elementContentRange];e *img = [UIImage imageNamed:elementContent]; (img) {ng *lines = @"n"; leading = ;g = leading;ame = elementContent;158.[processedStringreplaceCharactersInRange:NSMakeRange(on, + ) withString:lines];159.160.[_images addObject:currentSupernode];ange = NSMakeRange(on, [lines length]);162.} { (_verbose) NSLog(@"FTCoreTextView :%@ - Couldn't find image '%@' in main bundle", self, [NSValue valueWithRange:elementContentRange]);165.[processedString replaceCharactersInRange:tagRange withString:@""]; 166.}167.} {ange = NSMakeRange(ocation, on - ocation);170.[processedString replaceCharactersInRange:tagRange withString:@""]; 171.} ([edCharacter length] > 0) {173.[processedString insertString:edCharacter atIndex:on +];e newStyleRange = ange; += [edCharacter length]; ange = newStyleRange;177.} ( > 0) { (![/doc/
isEqualT oString:[selfdefaultTagNameForKey:FTCoreTextTagBullet]] || [[currentSupernodepreviousNode]/doc/
isEqualToString:[selfdefaultTagNameForKey:FTCoreTextTagBullet]]) {180.[processedString insertString:@"n" atIndex:ocation];e topSpacingStyleRange =NSMakeRange(ocation, [@"n" length]);TextStyle *topSpacingStyle = [[FTCoreT extStyle alloc] init];/doc/
= [NSStringstringWithFormat:@"_FTT opSpacingStyle_%@", /doc/
]; eHeight = ; eHeight = ; TextNode *topSpacingNode = [[FTCoreTextNode alloc] init]; = topSpacingStyle;ange = topSpacingStyleRange;190.191.[ode insertSubnode:topSpacingNode beforeNode:currentSupernode];192.193.[currentSupernodeadjustStylesAndSubstylesRangesByRange:topSpacingStyleRange];194.}195.}on = on + ; = [processedString length] - on; tSupernode = ode;199.}; FTCoreTextT agTypeSelfClose:202.{TextNode *newNode = [FTCoreTextNode new]; = style;205.[processedString replaceCharactersInRange:tagRangewithString:edCharacter];ange = NSMakeRange(on,[edCharacter length]);ocation = on;208.[currentSupernode addSubnode:newNode]; ()211.{ionary* blockDict = [NSDictionarydictionaryWithObjectsAndKeys:tagsComponents,@"components", [NSValue valueWithRange:NSMakeRange(on,)],@"range", nil nil];(blockDict);214.}on = on; = [processedString length] - on;217.};219.}220.}ange = NSMakeRange(0, [processedString length]);de = rootNode;sedString = processedString;225.}[objc]view plaincopyprint?1.//在指定的范围内,应⽤指定的格式2.- (void)applyStyle:(FTCoreTextStyle *)style inRange:(NSRange)styleRangeonString:(NSMutableAttributedString **)attributedString3.{4.[*attributedString addAttribute:(id):(id)/doc/
:styleRange];7.8.[*attributedString addAttribute:(id):(id):styleRange]; (rLined) {er *underline = [NSNumber numberWithInt:kCTUnderlineStyleSingle];14.[*attributedString addAttribute:(id):(id):styleRange];17.}Ref ctFont = CTFontCreateFromUIFont();20.21.[*attributedString addAttribute:(id):(__bridge id):styleRange];ase(ctFont);Alignment alignment = ignment;t maxLineHeight = eHeight;t minLineHeight = eHeight;t paragraphLeading = g;t paragraphSpacingBefore = ;t paragraphSpacingAfter = ;t paragraphFirstLineHeadIntent = ;t paragraphHeadIntent = ;t paragraphT ailIntent = ;36.37.//if (SYSTEM_VERSION_LESS_THAN(@"5.0")) {aphSpacingBefore = 0;39.//}x numberOfSettings = 9;t tabSpacing = 28.f; applyParagraphStyling = aragraphStyling; ([/doc/
isEqualToString:[selfdefaultTagNameForKey:FTCoreTextTagBullet]]) {aragraphStyling = YES;48.} if ([/doc/
isEqualToString:@"_FTBulletStyle"]) {aragraphStyling = YES;OfSettings++;cing = ;aphSpacingBefore = 0;aphSpacingAfter = 0;aphFirstLineHeadIntent = 0;aphTailIntent = 0;57.} if ([/doc/
hasPrefix:@"_FTT opSpacingStyle"]) {59.[*attributedString removeAttribute:(id)kCTParagraphStyleAttributeNamerange:styleRange];60.} (applyParagraphStyling) {TabRef tabArray[] = { CTTextTabCreate(0, tabSpacing, NULL) };yRef tabStops = CFArrayCreate( kCFAllocatorDefault, (const void**)tabArray, 1, &kCFTypeArrayCallBacks );ase(tabArray[0]);graphStyleSetting settings[] = {70.{kCTParagraphStyleSpecifierAlignment, sizeof(alignment), &alignment},71.{kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(CGFloat),&maxLineHeight},72.{kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(CGFloat),&minLineHeight},73.{kCTParagraphStyleSpecifierParagraphSpacingBefore, sizeof(CGFloat),graphSpacingBefore},74.{kCTParagraphStyleSpecifierParagraphSpacing, sizeof(CGFloat),graphSpacingAfter},
发布者:admin,转转请注明出处:http://www.yc00.com/news/1689265494a226469.html
评论列表(0条)