iOS中運用coreText 進行文字自適應

語歌發表於2016-08-04

先看看效果圖

這裡可以指定顯示的寬度,高度隨著文字的數量自動增加 看到這些是不是很開心,IM聊天記錄基本都是這樣的原理。 隨著輸入的字型自動增加,顯示的View的高度自動動態的增加

這裡介紹一下coreText,下面的一段話引用自唐巧部落格

CoreText 是用於處理文字和字型的底層技術。它直接和 Core Graphics(又被稱為 Quartz)打交道。Quartz 是一個 2D 圖形渲染引擎,能夠處理 OSX 和 iOS 中的圖形顯示。 Quartz 能夠直接處理字型(font)和字形(glyphs),將文字渲染到介面上,它是基礎庫中唯一能夠處理字形的模組。因此,CoreText 為了排版,需要將顯示的文字內容、位置、字型、字形直接傳遞給 Quartz。相比其它 UI 元件,由於 CoreText 直接和 Quartz 來互動,所以它具有高速的排版效果。 下圖是 CoreText 的架構圖,可以看到,CoreText 處於非常底層的位置,上層的 UI 控制元件(包括 UILabel,UITextField 以及 UITextView)和 UIWebView 都是基於 CoreText 來實現的。

搞MVC理念來實現

model裡面分三個類

YYGFrameParserConfig YYGFrameParser YYGCoreTextDat

Model

1.YYGFrameParserConfig 類

既然要把內容呈現出來,就必須知道內容呈現的內容的一下呈現方式吧! 該類就是幹這個的。

需要知道 要顯示的寬度

@property(nonatomic, assign) CGFloat width;

需要知道 要顯示的字型大小

@property(assign,nonatomic) CGFloat fontSize;

需要知道 要顯示的行間距離

@property(assign,nonatomic) CGFloat lineSpace;

需要知道 要顯示的字型顏色

@property(strong,nonatomic) UIColor * textColor;

程式碼如下


@interface YYGFrameParserConfig : NSObject

/**
 *  view顯示的寬度
 */
@property(nonatomic, assign) CGFloat width;

/**
 *  字型大小
 */
@property(assign,nonatomic) CGFloat fontSize;

/**
 *  行間距離
 */
@property(assign,nonatomic) CGFloat lineSpace;

/**
 *  字型顏色
 */
@property(strong,nonatomic) UIColor * textColor;


@end

--------------------
.m檔案
#import "YYGFrameParserConfig.h"

@implementation YYGFrameParserConfig

-(id)init
{
    self=[super init];
    if (self)
    {
        _width=200.0f;
        _fontSize=16.0f;
        _lineSpace=8.0f;
        _textColor=RGB(108, 108, 108);
    }
    return self;
}

@end複製程式碼

2.YYGFrameParser類

我們都知道任何呈現在手機螢幕上的資訊都是通過底層圖形渲染引擎來完成的,而在UIView裡面都是通過CTFrameDraw(CTFrameRef, CGContextRef)來畫在畫布上的。 所以該類就是生成CTFrameRef它的。 而生成這些要展現在View上的吧!就需要內容!和對內容展現的一些約束,寬度,字型大小啊,而這些在上一個類中我們已經完成啦!! 所以需要如下:

需要內容

需要YYGFrameParserConfig

而最開始介紹過,通過內容可以動態的生產高度這個才是關鍵啊 所以需要返回高度,和生成的CTFrameRef

返回高度

返回CTFrameRef

居然返回倆引數,好吧直接再弄一個model所以有了下一個model類

本類程式碼:

#import "YYGFrameParserConfig.h"
#import "YYGCoreTextData.h"


@interface YYGFrameParser : NSObject

+(YYGCoreTextData *)parseContent:(NSString *)content config:(YYGFrameParserConfig *)config;

@end

.m檔案

#import "YYGFrameParser.h"

@implementation YYGFrameParser

+(YYGCoreTextData *)parseContent:(NSString *)content config:(YYGFrameParserConfig *)config
{
    NSDictionary * dictionary=[self attributesWithConfig:config];
    NSAttributedString * string=[[NSAttributedString alloc]initWithString:content attributes:dictionary];
    // 建立 CTFramesetterRef 例項
    CTFramesetterRef framesetter=CTFramesetterCreateWithAttributedString((CFAttributedStringRef)string);
    // 獲得要繪製的區域的高度
    CGSize restrictSize=CGSizeMake(config.width, CGFLOAT_MAX);
    CGSize coreTextSize=CTFramesetterSuggestFrameSizeWithConstraints(framesetter,CFRangeMake(0, 0), nil, restrictSize, nil);
    CGFloat textHeight=coreTextSize.height;

     // 生成 CTFrameRef 例項
    CTFrameRef frameRef=[self createFrameWithFramesetter:framesetter config:config height:textHeight];
    // 將生成好的 CTFrameRef 例項和計算好的繪製高度儲存到 CoreTextData 例項中,最後返回 CoreTextData 例項
    YYGCoreTextData *data=[[YYGCoreTextData alloc]init];
    data.ctFram=frameRef;
    data.height=textHeight;

    CFRelease(frameRef);
    CFRelease(framesetter);

    return data;


}

+(NSDictionary *)attributesWithConfig:(YYGFrameParserConfig *)config
{

    NSMutableDictionary * dict=[NSMutableDictionary dictionary];
    //1
    CGFloat fontSize=config.fontSize;
    CTFontRef fontRef=CTFontCreateWithName((CFStringRef)@"ArialMT", fontSize, NULL);
    dict[(id)kCTFontAttributeName]=(__bridge id)fontRef;

    CFRelease(fontRef);
    //2
    CGFloat lineSpace=config.lineSpace;
    const CFIndex kNumberOfSetting=3;
    CTParagraphStyleSetting theSettings[kNumberOfSetting]={
        {kCTParagraphStyleSpecifierLineSpacingAdjustment,sizeof(CGFloat),&lineSpace},
        {kCTParagraphStyleSpecifierMaximumLineSpacing,sizeof(CGFloat),&lineSpace},
        {kCTParagraphStyleSpecifierMinimumLineSpacing,sizeof(CGFloat),&lineSpace}
    };
    CTParagraphStyleRef thePragraphRef=CTParagraphStyleCreate(theSettings, kNumberOfSetting);
    dict[(id)kCTParagraphStyleAttributeName]=(__bridge id)thePragraphRef;

    CFRelease(thePragraphRef);
    //3
    UIColor * textColor=config.textColor;
    dict[(id)kCTForegroundColorAttributeName]=(__bridge id)textColor.CGColor;

    return dict;
}

+(CTFrameRef )createFrameWithFramesetter:(CTFramesetterRef)framesetter config:(YYGFrameParserConfig *)config height:(CGFloat)height
{
    CGMutablePathRef path=CGPathCreateMutable();
    CGPathAddRect(path, NULL, CGRectMake(0, 0, config.width, height));

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

    CFRelease(path);

    return frame;
}



@end複製程式碼

3.YYGCoreTextData

它的出現來理由來自於上面的介紹

儲存生產的 CTFrameRef

@property(assign,nonatomic) CTFrameRef ctFram;

儲存生產的動態生成的高度

@property(assign,nonatomic) CGFloat height;

.m檔案
#import "YYGCoreTextData.h"

@implementation YYGCoreTextData

-(void)setCtFram:(CTFrameRef)ctFram
{
    if (_ctFram!=ctFram)
    {
        if (_ctFram!=nil)
        {
            CFRelease(_ctFram);
        }
        CFRetain(ctFram);
        _ctFram=ctFram;
    }
}

-(void)dealloc
{
    if (_ctFram!=nil)
    {
        CFRelease(_ctFram);
        _ctFram=nil;
    }

}

@end複製程式碼

View

.h檔案
@property(strong,nonatomic) YYGCoreTextData * data;

.m檔案
- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];
    CGContextRef context=UIGraphicsGetCurrentContext();
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    if (self.data)
    {
        CTFrameDraw(self.data.ctFram, context);
    }
}複製程式碼

好吧還需要從寫 UIView 的Category方法

在來一個類

#import <UIKit/UIKit.h>

@interface UIView (YYGView)

-(CGFloat)x;
-(void)setX:(CGFloat)x;

-(CGFloat)y;
-(void)setY:(CGFloat)y;

-(CGFloat)wide;
-(void)setWide:(CGFloat)wide;

-(CGFloat)height;
-(void)setHeight:(CGFloat)height;

@end

.m檔案

#import "UIView+YYGView.h"

@implementation UIView (YYGView)

-(CGFloat)x
{
    return self.frame.origin.x;
}
-(void)setX:(CGFloat)x
{
    self.frame=CGRectMake(x, self.y, self.wide, self.height);
}

-(CGFloat)y
{
    return self.frame.origin.y;
}
-(void)setY:(CGFloat)y
{
    self.frame=CGRectMake(self.x, y, self.wide, self.height);
}

-(CGFloat)wide
{
    return self.frame.size.width;
}
-(void)setWide:(CGFloat)wide
{
    self.frame=CGRectMake(self.x, self.y, wide, self.height);
}

-(CGFloat)height
{
    return self.frame.size.height;
}
-(void)setHeight:(CGFloat)height
{
    self.frame=CGRectMake(self.x, self.y, self.wide, height);
}


@end複製程式碼

YYGDisplayView 類

什麼Model啊,UIview的category的方法啊,都是為了把東西畫在畫面上,就是為YYGDisplayView打輔助的,輔助其實很很重要!!!

.m檔案

#import "YYGDisplayView.h"

@implementation YYGDisplayView

- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];
    CGContextRef context=UIGraphicsGetCurrentContext();

    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    if (self.data)
    {
        CTFrameDraw(self.data.ctFram, context);

    }

}

@end複製程式碼

Controller現在開始呼叫了

    YYGFrameParserConfig * config=[[YYGFrameParserConfig alloc]init];
    config.textColor=[UIColor purpleColor];
    config.width=self.YYGView.wide;

    YYGCoreTextData * data=[YYGFrameParser parseContent:@"韓美聯合參謀本部3日表示,朝鮮當天上午7時50分在黃海南道殷慄郡一帶朝日本海方向發射兩枚疑似“蘆洞”彈道導彈。其中一枚導彈發射不久便爆炸,另一枚導彈飛越朝鮮境內、最終落在距日本秋田縣男鹿半島250公里的日本專屬經濟區,總飛行距離約1000公里。日本共同社分析,朝鮮此舉除了針對美韓外,還意在制約日本政府。而韓軍分析,朝鮮可能通過發射導彈進行武力示威,抗議在韓部署薩德反導系統,並試圖助長韓國國內輿論分裂。" config:config];
    self.YYGView.data=data;
    self.YYGView.height=data.height;
    self.YYGView.backgroundColor=[UIColor lightGrayColor];複製程式碼

相關文章