Coretext实现富文本图文混排及Gif图片播放

CoreText是iOS3.2推出的一套文字排版和渲染框架,可以实现图文混排,富文本显示等效果。

CoreText中的几个重要的概念: 

  1. CTFont
  2. CTFontCollection
  3. CTFontDescriptor
  4. CTFrame
  5. CTFramesetter
  6. CTGlyphInfo
  7. CTLine
  8. CTParagraphStyle
  9. CTRun
  10. CTTextTab
  11. CTTypesetter

先来了解一下该框架的整体视窗组合图:

CTFrame 作为一个整体的画布(Canvas),其中由行(CTLine)组成,而每行可以分为一个或多个小方块(CTRun)。

注意:你不需要自己创建CTRun,Core Text将根据NSAttributedString的属性来自动创建CTRun。每个CTRun对象对应不同的属性,正因此,你可以自由的控制字体、颜色、字间距等等信息。

此外还有一点需要注意:一个CTRun是不能跨行的,若是一段文字拥有相同的属性,且跨行,则会被分在多个CTRun当中,每个CTRun拥有相同属性。

首先来看看使用Coretext的基本步骤:

第一步:

要有一个NSMutableAttributedString,用一个字符串来初始化NSMutableAttributedString。

NSMutableAttributedString  * _mString = [[NSMutableAttributedString alloc] initWithString:_text];

第二步:对NSMutableAttributedString进行属性设置。

   [_mString beginEditing];

[_mString addAttributes:textAttribute.attributeDic range:attr.range];

[_mString addAttribute:@"MTText" value:attr.text range:attr.range];

[_mString endEditing];

在这里有两种方式,一个是设置单个属性,一个是直接批量设置属性。属性的key值可以是自己定义的。

以下是一些常见的属性设置

1.设置字体属性

CTFontRef font = CTFontCreateWithName(CFSTR("Georgia"), 40, NULL);
 [_mString addAttribute:(id)kCTFontAttributeName value:(id)font range:NSMakeRange(0, 4)]; 

  2.设置斜体字    

CTFontRef font = CTFontCreateWithName((CFStringRef)[UIFont italicSystemFontOfSize:20].fontName, 14, NULL);
 [_mString addAttribute:(id)kCTFontAttributeName value:(id)font range:NSMakeRange(0, 4)];  

 

  3.设置连字

long number = 1;
CFNumberRef num = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt8Type,&number);
[mabstring addAttribute:(id)kCTLigatureAttributeName value:(id)num range:NSMakeRange(0, [str length])];  

  

  4.设置下划线

[_mString addAttribute:(id)kCTUnderlineStyleAttributeName value:(id)[NSNumber numberWithInt:kCTUnderlineStyleDouble] range:NSMakeRange(0, 4)];   

  

  5.设置下划线颜色

[_mString addAttribute:(id)kCTUnderlineColorAttributeName value:(id)[UIColor redColor].CGColor range:NSMakeRange(0, 4)];

  

  6.设置字体间隔

long number = 10;
CFNumberRef num = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt8Type,&number);
[_mString addAttribute:(id)kCTKernAttributeName value:(id)num range:NSMakeRange(10, 4)];  

 

最后是画出对应的图像了

  

    CGContextRef context = UIGraphicsGetCurrentContext();

CGContextTranslateCTM(context , 0 ,self.bounds.size.height);

CGContextScaleCTM(context, 1.0, -1.0);

  CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString(
                                                    (CFAttributedStringRef) _mString);

    CGMutablePathRef path = CGPathCreateMutable();
    CGRect rects = CGRectMake(0 , 0 ,self.bounds.size.width , self.bounds.size.height);
    CGPathAddRect(path,
                  NULL ,
                  rects);

    CTFrameRef frame = CTFramesetterCreateFrame(frameSetter,
                                                   CFRangeMake(0, 0),
                                                   path,
                                                   NULL);
    _frameRef = frame;

    CTFrameDraw(frame,context);

    CGPathRelease(path);
    CFRelease(frameSetter);

这里有几点比较需要注意:

  1.坐标转换问题。在使用CGcontext进行绘制时坐标轴圆点在屏幕左下方,而UIKit得坐标系圆点在左上方,需要对此进行转换。此外,在后面的操作中也要用到坐标变换。

  2.文字的绘画区域不是整个context,而是在CTFrameRef中,后面我们还会碰到相关的问题。

CTFrameRef frame = CTFramesetterCreateFrame(frameSetter,
                                                   CFRangeMake(0, 0),
                                                   path,
                                                   NULL);

现在我们已经能设置文字的基本属性了,但富文本中还有比较重要的一个应用:图文混排

我们先来说说实现图文混排的基本思路。

coretext是直接绘制在layer层上的,我们可以在drawRect 方法中直接画一张图片,只要将图片放在适合的位置,就实现了图文混排。

现在的关键是如何计算出图片所在的位置,此外,当图片添加的时候,文字的排版要如何调整问题。

为了能插入图片,首先我们要设置一个占位符,正常设置为空格符,因为图片如果没有完全覆盖那个区域,可能显示出占位符。当然你也可以任意设置一个字符并将其

的颜色设置为clearColor。设置占位符后我们要为它设置属性,

记住,要为它设置一个单独的属性,不能与相邻的字符属性一致,因为这样系统可能因此将他们合并在一个CTRun当中。

占位符最好只设置一个,如果占位符是多个的话,有可能占位符处于不同行,会被分成两个CTRun,此时图片就可能超出屏幕边界。

NSMutableAttributedString *replaceStr = [[NSMutableAttributedString alloc] initWithString:@"1"];

UIColor *color = [UIColor clearColor];

NSRange range = NSMakeRange(_mString.length - 1, 1);

[replaceStr addAttribute:(id)color.CGColor value:(id)kCTForegroundColorAttributeName range:range];

[_mString appendAttributedString:replaceStr];

设置完占位符基本属性后,我们需要设置占位符对应得CTRun的回调方法来设置CTRun的大小,以适应图片。 CTRunDelegateCallbacks imageCallBacks;

 imageCallBacks.version = kCTRunDelegateVersion1;
 imageCallBacks.dealloc = RunDelegateDeallocCallback;
 imageCallBacks.getAscent = RunDelegateGetAsent;
 imageCallBacks.getDescent = RunDelegateGetDescent;
 imageCallBacks.getWidth = RunDelegateGetWidthCallBack;
 //传入的参数attr.text可以在回调方法中使用
 CTRunDelegateRef runDelegate = CTRunDelegateCreate(&imageCallBacks,
                                                               (__bridge void *)(attr.text));
 CTRunDelegateGetRefCon(runDelegate);
 [_mString addAttribute:(NSString *)kCTRunDelegateAttributeName
                              value:(__bridge id)runDelegate
                                          range:attr.range];
 [_mString addAttribute:@"imageName" value:attr.text range:attr.range];
 CFRelease(runDelegate);

void RunDelegateDeallocCallback{

}

CGFloat RunDelegateGetAsent(void *refCon) {


NSString *imageName = (__bridge NSString *)(refCon);

return [UIImage imageNamed:imageName].size.height;

}

CGFloat RunDelegateGetDescent(void *refCon) {

  return 0;

}

CGFloat RunDelegateGetWidth(void *refCon) {

   NSString *imageName = (__bridge NSString *)(refCon);

return [UIImage imageNamed:imageName].size.width;

}

在设置好属性还有回调方法之后,就可以开始绘制图片了。

基本的思路是获取文本的每一行,再获取每一个CTRun,根据属性来判断是否是绘制图片的点,是的话则获取绘画区域,绘制图片。

    CFArrayRef lines = CTFrameGetLines(_frameRef);
    CGPoint origins[CFArrayGetCount(lines)];
    CTFrameGetLineOrigins(_frameRef, CFRangeMake(0, 0), origins);

    NSMutableArray *attrArray = [[NSMutableArray alloc] init];
    for (int i = 0; i < CFArrayGetCount(lines); i ++) {

        CTLineRef line = CFArrayGetValueAtIndex(lines, i);

        CFArrayRef runs = CTLineGetGlyphRuns(line);

        for (int k = 0; k < CFArrayGetCount(runs); k ++) {

            CTRunRef run = CFArrayGetValueAtIndex(runs, k);
            NSDictionary *attri = (NSDictionary *)CTRunGetAttributes(run);
            NSString *imageName = [attri objectForKey:@"imageName"];

            if (imageName) {
                CGFloat runAsent;
                CGFloat runDescent;
                CGPoint origin = origins[i];
                CGRect runRect;

                runRect.size.width = CTRunGetTypographicBounds(run,
                                                               CFRangeMake(0, 0),
                                                               &runAsent,
                                                               &runDescent,
                                                               NULL);
                CGFloat offset = CTLineGetOffsetForStringIndex(line,
                                                               CTRunGetStringRange(run).location,
                                                               NULL);
                runRect = CGRectMake(origin.x + offset,
                                     origin.y - runDescent,
                                     runRect.size.width,
                                     runAsent + runDescent);
                

          UIImage *image = [UIImage imageNamed:imageName];

CGContextDrawImage(context, runRect, image.CGImage);

            }
        }
    }

这里有个需要注意的点,我们取到得位置是相对于整个画布,而不是相对于所在的View的位置

CTFrameRef frame = CTFramesetterCreateFrame(frameSetter,
                                                   CFRangeMake(0, 0),
                                                   path,
                                                   NULL);

因此,如果CTFrameref如果有不在原点,计算时需要加上这部分的偏移量。

点击事件和图片绘制差不多,获取点击的点,进行坐标变换

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    UITouch *touch = touches.anyObject;
    CGPoint point = [touch locationInView:self];  //坐标变换
    CGPoint location = CGPointMake(point.x, self.bounds.size.height - point.y);

    MTLabelAttribute *attr = [self getAttributeByLocation:location];
    _lastAttr = attr;
    if (attr) {
        if ([self.delegate respondsToSelector:@selector(clickWithAttibute:andText:)]) {
            [self.delegate clickWithAttibute:attr andText:attr.text];
        }

        [self setAttributeWithType:@"hightlight" andAttribute:attr];
    }
       //判断point是否在点击的文字范围内
}

先判断在哪一行,然后判断点击的点在哪一个CTRun上。在这里要注意所要实现点击的字符串可能跨行的情况。

- (MTLabelAttribute *)getAttributeByLocation:(CGPoint) point{

    NSArray *lines = (NSArray *)CTFrameGetLines(_frameRef);

    CGPoint origins[lines.count];
    CTFrameGetLineOrigins(_frameRef, CFRangeMake(0, 0), origins);
    CTLineRef ref;

    int count = 0;
   //判断所在的行
    if (point.y < origins[lines.count - 1].y) {
        return nil;
    }

    for (int i = 1; i < lines.count ; i ++) {

        CGFloat minY = origins[i].y;
        CGFloat maxY = origins[i - 1].y;
        if (point.y >= minY && point.y <=  maxY) {
            count = i ;
            break;
        }
    }

    ref = (__bridge CTLineRef)lines[count];
    CGPoint origin = origins[count];
    NSArray *ctRuns = (NSArray *)CTLineGetGlyphRuns(ref);

 //判断所在的CTRun
    for (int k = 0; k < ctRuns.count; k ++) {
        CTRunRef runTest = (__bridge CTRunRef)([ctRuns objectAtIndex:k]);

        CGFloat offset = CTLineGetOffsetForStringIndex((CTLineRef)lines[count],
                                                       CTRunGetStringRange(runTest).location,
                                                       NULL) + 0.0;

        CGPoint firstPoint = CGPointMake(origin.x + offset , origin.y);

        CGFloat ascent;
        CGFloat descent;
        CGFloat leading;

        CGFloat width =  CTRunGetTypographicBounds(runTest, CFRangeMake(0, 0),
                                                   &ascent,
                                                   &descent,
                                                   &leading);

        if ( point.x >= firstPoint.x
            &&point.x <= firstPoint.x + width
            &&point.y <= origin.y + ascent
            &&point.y >= origin.y ) {

            NSDictionary *dic = (NSDictionary *)CTRunGetAttributes(runTest);
            NSString *string = [dic objectForKey:@"MTText"];
            CFRange cfRange = CTRunGetStringRange(runTest);

            if ([dic objectForKey:@"imageName"]) {
                return [self getAttributesWithRange:NSMakeRange(cfRange.location, cfRange.length)];
            }
                   //跨行情况处理
            NSString *subString = [self.text substringWithRange:
                                   NSMakeRange(cfRange.location, cfRange.length)];

            NSRange range;
            NSRange subStringRange = [string rangeOfString:subString];

            range = NSMakeRange(cfRange.location - subStringRange.location, string.length);

            return [self getAttributesWithRange:range];
        }

    }
    return nil;
}

最后,是Gif的显示

  gif是比较特殊的一种情况,处理起来也比较麻烦。对于gif有两种展示方式,一种是用一个专门的UIView来展示,然后用添加subView的方式使用。用这种方式可以使用

第三方的框架,使用UIWebView播放等,也可以自己写,下面是使用UIImageView的方式:详情可看http://www.cocoachina.com/bbs/read.php?tid=124430

UIImageView *gifImageView = [[UIImageView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    NSArray *gifArray = [NSArray arrayWithObjects:[UIImage imageNamed:@"1"],
                                                  [UIImage imageNamed:@"2"],
                                                  [UIImage imageNamed:@"3"],
                                                  [UIImage imageNamed:@"4"],
                                                  [UIImage imageNamed:@"5"],
                                                  [UIImage imageNamed:@"6"],
                                                  [UIImage imageNamed:@"7"],
                                                  [UIImage imageNamed:@"8"],
                                                  [UIImage imageNamed:@"9"],
                                                  [UIImage imageNamed:@"10"],
                                                  [UIImage imageNamed:@"11"],
                                                  [UIImage imageNamed:@"12"],
                                                  [UIImage imageNamed:@"13"],
                                                  [UIImage imageNamed:@"14"],
                                                  [UIImage imageNamed:@"15"],
                                                  [UIImage imageNamed:@"16"],
                                                  [UIImage imageNamed:@"17"],
                                                  [UIImage imageNamed:@"18"],
                                                  [UIImage imageNamed:@"19"],
                                                  [UIImage imageNamed:@"20"],
                                                  [UIImage imageNamed:@"21"],
                                                  [UIImage imageNamed:@"22"],nil];
    gifImageView.animationImages = gifArray; //动画图片数组
    gifImageView.animationDuration = 5; //执行一次完整动画所需的时长
    gifImageView.animationRepeatCount = 1;  //动画重复次数
    [gifImageView startAnimating];
    [self.view addSubview:gifImageView];
    [gifImageView release]; 

使用UIImageView的方式是固定的时间间隔,但gif并非每一帧的间隔都一样,因此有些情况可能达不到最好的播放效果。

在IOS7之后可以使用TextKit中的attachment,直接添加UIWebView播放Gif图片,YYTextKit中也有类似的实现。

如果我们要手动实现的话,那就只好一帧一帧的往屏幕上画了。我们首先来看看继承UIView的实现方式,记住不能继承自caLayer,虽然最后是在layer层绘画,

但直接继承calyer,然后用addsublayer方法显示,图像会有重影,大概UIView中有对其进行处理。

首先用一个类对Gif图片进行解析

@interface MTGifAttribute : NSObject

@property (nonatomic, strong, readonly) NSArray *imageFrames;
@property (nonatomic, strong, readonly) NSArray *properties;
@property (nonatomic, strong, readonly) NSArray *delayTimes;

@property (nonatomic, assign, readwrite) UIView<MTGifProtocol> * delegate;
@property (nonatomic, assign, readwrite) CGRect frame;
//@property (nonatomic, copy) NSString *path;
@property (nonatomic, assign, readonly) NSInteger index;

- (void)setImageInfoWithFilePath:(NSString *)path;
- (void)startAnitation;

@end

解析方法

@implementation MTGifAttribute

- (instancetype)init{

    self = [super init];
    if (self) {
        _index = 0;
    }
    return self;
}

- (void)setImageInfoWithFilePath:(NSString *)path {

    NSMutableArray *imageFrames = [[NSMutableArray alloc] init];
    NSMutableArray *delayTimes = [[NSMutableArray alloc] init];

    NSURL *filrUrl = [NSURL fileURLWithPath:path];
    CFURLRef cfUrl = (__bridge CFURLRef)filrUrl;

    CGImageSourceRef gifSource = CGImageSourceCreateWithURL(cfUrl, NULL);
    NSInteger count = CGImageSourceGetCount(gifSource);

    for (int i = 0; i < count; i++){
        CGImageRef frame = CGImageSourceCreateImageAtIndex(gifSource,
                                                           i,
                                                           NULL);

        [imageFrames addObject:(__bridge id)frame];
        CGImageRelease(frame);

        NSDictionary *dic = CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(gifSource,
                                                                                 i,
                                                                                 NULL));

        NSDictionary *gifDic =[dic valueForKey:(NSString *)kCGImagePropertyGIFDictionary];
        [delayTimes addObject:[gifDic objectForKey:(NSString *)kCGImagePropertyGIFDelayTime]];

    }

    NSDictionary *dic = CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(gifSource,
                                                                             0,
                                                                             NULL));

    CGFloat gifWidth = (CGFloat)[[dic valueForKey:(NSString*)kCGImagePropertyPixelWidth]
                                 floatValue];

    CGFloat gifHeight = (CGFloat)[[dic valueForKey:(NSString*)kCGImagePropertyPixelHeight]
                                  floatValue];

    _frame = CGRectMake(0, 0, gifWidth, gifHeight);
    _imageFrames = imageFrames;
    _delayTimes = delayTimes;
}

- (void)startAnitation {
    [self changeImage];
}

- (void)changeImage {
        //代理方法,调用setNeeddisplay
    if ([self.delegate respondsToSelector:@selector(disPlayInRect:)]) {
        [self.delegate disPlayInRect:self.frame];
    }else{

        return;
    }

    _index ++;
    _index = _index % self.imageFrames.count;
    CGFloat delay = [[self.delayTimes objectAtIndex:_index] floatValue];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
                       (int64_t)(delay * NSEC_PER_SEC)),
                       dispatch_get_main_queue(), ^{

                           [self changeImage];

                       });
}

图片索引切换方法

- (void)startAnitation {
    [self changeImage];
}

- (void)changeImage {

    if ([self.delegate respondsToSelector:@selector(disPlayInRect:)]) {
        [self.delegate disPlayInRect:self.frame];
    }else{

        return;
    }

    _index ++;
    _index = _index % self.imageFrames.count;
    CGFloat delay = [[self.delayTimes objectAtIndex:_index] floatValue];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
                       (int64_t)(delay * NSEC_PER_SEC)),
                       dispatch_get_main_queue(), ^{

                           [self changeImage];

                       });
}

继承UIView,重写drawRect方法。

- (void)drawRect:(CGRect)rect{

    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(ctx , 0 ,self.frame.size.height);
    CGContextScaleCTM(ctx, 1.0, -1.0); 

    UIImage *image = [_gifImage.imageFrames objectAtIndex:_index];
    CGImageRef cgimage = image.CGImage;
    UIGraphicsBeginImageContext(CGSizeMake(self.frame.size.width, self.frame.size.height));
    CGContextDrawImage(ctx, _gifImage.frame, cgimage);

}

至此Gif的显示已经完成,但有些时候我们不想单独用一个view来显示。而是想用coretext图文混排的方式来显示,将gif和文字显示在同一个view。

此时的方式和用一个单独的view显示一样,只是绘画的对象不同而已。

比较需要注意的一个点是,在每次重绘的时候,因为gif不断地重绘,如果每次都刷新整个View的话有可能会造成性能问题。因此可以使用

[self setNeedsDisplayInRect:rect];方法,只刷新gif所在区域。

当然,在这种情况下也要万分注意左边变换问题,如果刷新的区域,与变换坐标后的绘画区域不对应,图片会消失或者只显示部分。

时间: 2024-11-07 18:35:16

Coretext实现富文本图文混排及Gif图片播放的相关文章

富文本,图文混排

1.控制器代码 #import "ViewController.h" #import "NSAttributedString+Emoji.h" @interface ViewController () @property(nonatomic,strong)UILabel*labelText; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; UILabel *

iOS-Swift3富文本(UILable文本图文混排)

转载注明出处:http://blog.csdn.net/qxuewei/article/details/53213636 介绍下iOS开发中常用的符文布图文混排 需求: 邱学伟是大帅哥(加个笑脸图片) 邱学伟:红色背景绿色字体加粗显示 是:蓝色字体 10号小字体 大帅哥:灰色42号字体 UILabel中显示结果: 原谅我跟哥们开玩笑起的low爆了的项目名 核心代码: //需求 邱学伟是大帅哥(加个笑脸图片) 邱学伟:红色背景绿色字体加粗显示 是:蓝色字体 10号小字体 大帅哥:灰色42号字体 f

DIV+CSS 图文混排的图片居中办法

不少人为了让 Div 图文混排的图片可以居中,给 IMG 套各式各样的 SPAN.DIV.LI 等等,以便于使用 text-align来进行居中. 1 <div>图文混排 2 <br> 3 <span style="text-align:center"><img src="http://www.baidu.com/img/baidu_jgylogo3.gif"></span> 4 </div>

静态页面制作:8HTML浮动腾挪概念(铺垫二:图文混排)

.paragraph { width: 80%; margin: 50px auto; color: #666; font-size: 20px; text-align: left; line-height: 200%; padding: 30px } 现在那我们来说说第二个铺垫. 第二个铺垫叫做图文混排.什么是图文混排呢,这个东西我们还需要花些时间来分析一下的.首先看下图: 其实上图就是一个例子,然而这个例子其实在我们浏览网页,或者看一些微博.博客等文章时,我们发现这种格式很常见.这种有内容.

超全css解决方案之图文混排解决方案

第一种方法:背景图片法 这种方法适用于非动态内容,图片用于装饰的情况下.主要是设置父元素的padding的宽度为背景图片的宽度加上图片和文字的间距,然后把背景图片定位到padding里面就可以了 图文混排demo1,背景图片法    //因是转载的,我又是菜鸟,下面图片乱了,正确的是图片跟文字都在一行. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Pel

CoreText实现图文混排之点击事件-b

CoreText实现图文混排之点击事件 主要思路 我们知道,CoreText是基于UIView去绘制的,那么既然有UIView,就有 -(void)touchesBegan:(NSSet<UITouch *> )touches withEvent:(UIEvent )event方法,我们呢,就是基于这个方法去做点击事件的. 通过touchBegan方法拿到当前点击到的点,然后通过坐标判断这个点是否在某段文字上,如果在则触发对应事件. 上面呢就是主要思路.接下来呢,我们来详细讲解一下.还是老规矩

CoreText实现图文混排之点击事件

今天呢,我们继续把CoreText图文混排的点击事件补充上,这样我们的图文混排也算是圆满了. 哦,上一篇的链接在这里 http://www.jianshu.com/p/6db3289fb05d CoreText实现图文混排.所有需要用到的准备知识都在上一篇,没有赶上车的朋友可以去补个票~ 上正文. CoreText做图文混排之点击事件 主要思路 我们知道,CoreText是基于UIView去绘制的,那么既然有UIView,就有 -(void)touchesBegan:(NSSet)touches

[Swift通天遁地]八、媒体与动画-(13)CoreText框架实现图文混排

本文将演示CoreText框架实现图文混排.CoreText(富文本)框架并不支持图片的绘制, 需要借助Core Graphics框架来进行图片的绘制. 图文混排的实现原理非常简单,就是在一个富文本中插入一个占位符, 表示此处需要插入一张图片.然后再由另一个图形绘制框架, 在占位符所在位置绘制指定的图片. 在项目文件夹上点击鼠标右键,弹出右键菜单. [New File]->[Cocoa Touch]->[Next]-> [Class]:CTImageView [Subclass of]:

简单的Coretext 图文混排

在很多新闻类或有文字展示的应用中现在都会出现图文混排的界面例如网易新闻等,乍一看去相似一个网页,其实这样效果并非由UIWebView 加载网页实现.现在分享一种比较简单的实现方式 iOS sdk中为我们提供了一套完善的文字排版开发组件:CoreText.CoreText库中提供了很多的工具来对文本进行操作,例如CTFont.CTLine.CTFrame等.利用这些工具可以对文字字体每一行每一段落进行操作. 此例中默认图片都在右上方,且为了美观和开发简便设定所占宽度都相同. 1.