本文講的是如何計算CoreText繪製的內容在指定寬度的場景下內容的大小,客戶端能夠經過公有的接口在圖層渲染以前或獲得內容的尺寸進行預先的佈局。此外,如今自動佈局的應用場景愈來愈多了,使用CoreText繪圖的View如何與自動佈局進行兼容也是本文會涉及到的話題git
其它文章:
CoreText 入門(一)-文本繪製
CoreText入門(二)-繪製圖片
CoreText進階(三)-事件處理
CoreText進階(四)-文字行數限制和顯示更多
CoreText進階(五)- 文字排版樣式和效果
CoreText進階(六)-內容大小計算和自動佈局
CoreText進階(七)-添加自定義View和對其程序員
Demo:CoreTextDemo佈局
下面的圖片展現了使用三種方式佈局的效果.net
實現效果的代碼:code
{ CGRect frame = CGRectMake(0, 20, self.view.bounds.size.width, 100); YTDrawView *textDrawView = [[YTDrawView alloc] initWithFrame:frame]; textDrawView.backgroundColor = [UIColor whiteColor]; textDrawView.text = @"手動佈局手動計算高度:\n這是一個最好的時代,也是一個最壞的時代;這是明智的時代,這是愚昧的時代;這是信任的紀元,這是懷疑的紀元;這是光明的季節,這是黑暗的季節;這是但願的春日,這是失望的冬日;咱們面前應有盡有,咱們面前一無全部;咱們都將直上天堂,咱們都將直下地獄。"; textDrawView.textColor = [UIColor redColor]; textDrawView.font = [UIFont systemFontOfSize:16]; CGSize size = [textDrawView sizeThatFits:CGSizeMake(frame.size.width, MAXFLOAT)]; textDrawView.frame = CGRectMake(CGRectGetMinX(frame), CGRectGetMinY(frame), size.width, size.height); [self.view addSubview:textDrawView]; } { YTDrawView *textDrawView = [[YTDrawView alloc] initWithFrame:CGRectZero]; textDrawView.backgroundColor = [UIColor whiteColor]; textDrawView.text = @"自動佈局自動計算高度:\n這是一個最好的時代,也是一個最壞的時代;這是明智的時代,這是愚昧的時代;這是信任的紀元,這是懷疑的紀元;這是光明的季節,這是黑暗的季節;這是但願的春日,這是失望的冬日;咱們面前應有盡有,咱們面前一無全部;咱們都將直上天堂,咱們都將直下地獄。"; textDrawView.textColor = [UIColor redColor]; textDrawView.font = [UIFont systemFontOfSize:16]; textDrawView.frame = CGRectMake(0, 0, self.view.bounds.size.width, 0); [self.view addSubview:textDrawView]; [textDrawView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.equalTo(self.view); make.top.equalTo(self.view).offset(200); }]; } { YTDrawView *textDrawView = [[YTDrawView alloc] initWithFrame:CGRectZero]; textDrawView.backgroundColor = [UIColor whiteColor]; textDrawView.text = @"自動佈局限制高度:\n這是一個最好的時代,也是一個最壞的時代;這是明智的時代,這是愚昧的時代;這是信任的紀元,這是懷疑的紀元;這是光明的季節,這是黑暗的季節;這是但願的春日,這是失望的冬日;咱們面前應有盡有,咱們面前一無全部;咱們都將直上天堂,咱們都將直下地獄。"; textDrawView.textColor = [UIColor redColor]; textDrawView.font = [UIFont systemFontOfSize:16]; textDrawView.frame = CGRectMake(0, 0, self.view.bounds.size.width, 0); [self.view addSubview:textDrawView]; [textDrawView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.equalTo(self.view); make.top.equalTo(self.view).offset(400); make.height.mas_equalTo(64); }]; }
計算內容大小須要重寫UIView
的方法sizeThatFits
,返回一個CGSize
。對象
CTLineGetStringRange
方法獲取最後一行CTLineRef
(若是設置的行數限制須要使用,不然不用這個步驟)的內容顯示的範圍,返回一個CFRange
對象CTFramesetterSuggestFrameSizeWithConstraints
方法計算指定範圍內容的大小,第二個參數是CFRange
類型,在設置行數限制的狀況傳遞上面獲取到的CFRange
對象,沒有行數限制的狀況直接設置一個空的CFRange
對象(CFRangeMake(0, 0))- (CGSize)sizeThatFits:(CGSize)size { NSAttributedString *drawString = self.data.attributeStringToDraw; if (drawString == nil) { return CGSizeZero; } CFAttributedStringRef attributedStringRef = (__bridge CFAttributedStringRef)drawString; CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attributedStringRef); CFRange range = CFRangeMake(0, 0); if (_numberOfLines > 0 && framesetter) { CGMutablePathRef path = CGPathCreateMutable(); CGPathAddRect(path, NULL, CGRectMake(0, 0, size.width, size.height)); CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL); CFArrayRef lines = CTFrameGetLines(frame); if (nil != lines && CFArrayGetCount(lines) > 0) { NSInteger lastVisibleLineIndex = MIN(_numberOfLines, CFArrayGetCount(lines)) - 1; CTLineRef lastVisibleLine = CFArrayGetValueAtIndex(lines, lastVisibleLineIndex); CFRange rangeToLayout = CTLineGetStringRange(lastVisibleLine); range = CFRangeMake(0, rangeToLayout.location + rangeToLayout.length); } CFRelease(frame); CFRelease(path); } CFRange fitCFRange = CFRangeMake(0, 0); CGSize newSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, range, NULL, size, &fitCFRange); if (framesetter) { CFRelease(framesetter); } return newSize; }
自動佈局會調用intrinsicContentSize
方法獲取內容的大小,因此重寫這個方法,這個方法調用sizeThatFits
方法獲取到內容的大小,而後返回便可,這裏有個主意的地方sizeThatFits
方法須要一個CGSize
的參數,這個參數若是在初始化YTDrawView
的時候沒有傳遞Frame
,size
的width
值會爲0,這樣使用sizeThatFits
方法時不對的,最終只能顯示一行,因此在初始化YTDrawView
的時候須要設置一個frame
,確保frame
中的width
不能爲0,須要時最後須要顯示的內容的寬度,這樣才能正常進行自動佈局blog
- (CGSize)intrinsicContentSize { return [self sizeThatFits:CGSizeMake(self.bounds.size.width, MAXFLOAT)]; }
使用自動佈局的事例代碼,注意點就是須要傳遞一個frame,其實在自動佈局模式下只要用到width,其它值爲0便可接口
YTDrawView *textDrawView = [[YTDrawView alloc] initWithFrame:CGRectZero]; textDrawView.backgroundColor = [UIColor whiteColor]; textDrawView.text = @"自動佈局限制高度:\n這是一個最好的時代,也是一個最壞的時代;這是明智的時代,這是愚昧的時代;這是信任的紀元,這是懷疑的紀元;這是光明的季節,這是黑暗的季節;這是但願的春日,這是失望的冬日;咱們面前應有盡有,咱們面前一無全部;咱們都將直上天堂,咱們都將直下地獄。"; textDrawView.textColor = [UIColor redColor]; textDrawView.font = [UIFont systemFontOfSize:16]; // 這一步很重要,須要傳遞一個frame,其實在自動佈局模式下只要用到width,其它值爲0便可 textDrawView.frame = CGRectMake(0, 0, self.view.bounds.size.width, 0); [self.view addSubview:textDrawView]; [textDrawView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.equalTo(self.view); make.top.equalTo(self.view).offset(400); make.height.mas_equalTo(64); }];
只有20%的iOS程序員能看懂:詳解intrinsicContentSize 及 約束優先級/content Hugging/content Compression Resistance事件