本章介紹了一些常見的文本布局操作,并說明了如何使用Core Text
執(zhí)行它們。本章包含以下代碼清單操作:
- 列出一個段落
- 簡單的文字標簽
- 列式布局
- 手動換行
- 應用段落樣式
- 在非矩形區(qū)域中顯示文本
列出一個段落
排版中最常見的操作之一是在任意大小的矩形區(qū)域內(nèi)布置多線段。 Core Text
使此操作變得簡單,只需要幾行特定于Core Text
的代碼。要布置段落,您需要繪制圖形上下文,矩形路徑以提供布置文本的區(qū)域以及屬性字符串。此示例中的大多數(shù)代碼都需要創(chuàng)建和初始化上下文,路徑和字符串。完成此操作后,Core Text
只需要三行代碼即可完成布局。
清單2-1
中的代碼顯示了段落的布局方式。此代碼可以駐留在UIView
子類的drawRect:
方法中(OS X
中的NSView
子類)。
清單2-1排版一個簡單的段落(下面是蘋果文檔給出的例子)
// 在iOS中初始化圖形上下文。
CGContextRef context = UIGraphicsGetCurrentContext();
// 翻轉(zhuǎn)上下文坐標,僅限iOS。
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// 在OS X中初始化圖形上下文是不同的:
// CGContextRef context =
// (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
// 設置文本矩陣。
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
// 創(chuàng)建一個限定要繪制文本的區(qū)域的路徑。
// 路徑不需要一定是矩形。
CGMutablePathRef path = CGPathCreateMutable();
// 在這個簡單的例子中,初始化一個矩形路徑。
CGRect bounds = CGRectMake(10.0, 10.0, 200.0, 200.0);
CGPathAddRect(path, NULL, bounds );
// 初始化一個字符串。
CFStringRef textString = CFSTR("Hello, World! I know nothing in the world that has as much power as a word. Sometimes I write one, and I look at it, until it begins to shine.");
// 創(chuàng)建一個最大長度為0的可變屬性字符串。
// 最大長度是關(guān)于要保留多少內(nèi)部存儲空間的提示。
// 0表示沒有提示。
CFMutableAttributedStringRef attrString =
CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
// 將textString復制到新創(chuàng)建的attrString中
CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0),
textString);
// 創(chuàng)建一個將作為屬性添加到attrString的顏色。
CGColorSpaceRef rgbColorSpace =
CGColorSpaceCreateDeviceRGB();
CGFloat components[] = { 1.0, 0.0, 0.0, 0.8 };
CGColorRef red = CGColorCreate(rgbColorSpace, components);
CGColorSpaceRelease(rgbColorSpace);
// 將前12個字符的顏色設置為紅色。
CFAttributedStringSetAttribute(attrString, CFRangeMake(0, 12),
kCTForegroundColorAttributeName, red);
// 使用屬性字符串創(chuàng)建frame集合。
CTFramesetterRef framesetter =
CTFramesetterCreateWithAttributedString(attrString);
CFRelease(attrString);
// 創(chuàng)建一個frame。
CTFrameRef frame =
CTFramesetterCreateFrame(framesetter,
CFRangeMake(0, 0), path, NULL);
// 在給定的上下文中繪制指定的幀。
CTFrameDraw(frame, context);
// 釋放我們使用的對象。
CFRelease(frame);
CFRelease(path);
CFRelease(framesetter);
下面是我的demo里面的相關(guān)代碼(swift
語言):
override func draw(_ rect: CGRect) {
super.draw(rect)
// 在iOS中初始化圖形上下文。
let context = UIGraphicsGetCurrentContext()
// 翻轉(zhuǎn)上下文坐標,僅限iOS。
context?.translateBy(x: 0, y: self.bounds.size.height)
context?.scaleBy(x: 1.0, y: -1.0)
// 設置文本矩陣。
context?.textMatrix = .identity
// 創(chuàng)建一個限定要繪制文本的區(qū)域的路徑。
// 路徑不需要一定是矩形。
let path = CGMutablePath()
// 在這個簡單的例子中,初始化一個矩形路徑。
let bounds = self.bounds
path.addRect(bounds)
// 初始化一個字符串。
let string = "Hello, World! I know nothing in the world that has as much power as a word. Sometimes I write one, and I look at it, until it begins to shine."
let textString: CFString = string as CFString
// 創(chuàng)建一個最大長度為0的可變屬性字符串。
// 最大長度是關(guān)于要保留多少內(nèi)部存儲空間的提示。
// 0表示沒有提示。
let attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0)
// 將textString復制到新創(chuàng)建的attrString中
CFAttributedStringReplaceString(attrString, CFRangeMake(0, 0), textString)
// 創(chuàng)建一個將作為屬性添加到attrString的顏色。
let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
let components: Array<CGFloat> = [1.0,1.0, 1.0, 0.8]
let red = CGColor(colorSpace: rgbColorSpace, components: components)
// 將前12個字符的顏色設置為紅色。
CFAttributedStringSetAttribute(attrString, CFRangeMake(0, string.count), NSAttributedString.Key.foregroundColor as CFString, red)
// 使用屬性字符串創(chuàng)建frame集合。
let framesetter = CTFramesetterCreateWithAttributedString(attrString!)
// 創(chuàng)建一個frame
let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, nil)
// 在給定的上下文中繪制指定的幀。
CTFrameDraw(frame, context!)
}
簡單的文字標簽
另一種常見的排版操作是繪制單行文本以用作用戶界面元素的標簽。在Core Text
中,這只需要兩行代碼:一行用于創(chuàng)建具有CFAttributedString
的行對象,另一行用于將行繪制到圖形上下文中。
清單2-2
顯示了如何在UIView
或NSView
子類的drawRect: 方法中完成此操作。該列表省略了純文本字符串,字體和圖形上下文的初始化,本文檔中其他列表中顯示的操作。它顯示了如何創(chuàng)建屬性字典并使用它來創(chuàng)建屬性字符串。 (字體創(chuàng)建顯示在Creating Font Descriptors 和Creating a Font from a Font Descriptor 。)
清單2-2
排版一個簡單的文本標簽
CFStringRef string; CTFontRef font; CGContextRef context;
// 初始化字符串,字體和上下文
CFStringRef keys[] = { kCTFontAttributeName };
CFTypeRef values[] = { font };
CFDictionaryRef attributes =
CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys,
(const void**)&values, sizeof(keys) / sizeof(keys[0]),
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFAttributedStringRef attrString =
CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
CFRelease(string);
CFRelease(attributes);
CTLineRef line = CTLineCreateWithAttributedString(attrString);
// 設置文本位置并將線條繪制到圖形上下文中
CGContextSetTextPosition(context, 10.0, 10.0);
CTLineDraw(line, context);
CFRelease(line);
下面是我自己的demo中的設置(swift語言)
override func draw(_ rect: CGRect) {
super.draw(rect)
let text = "Test content Test content Test "
let string: CFString = text as CFString
let font: CTFont = CTFontCreateWithName(string, 50, nil)
let context = UIGraphicsGetCurrentContext()
context?.translateBy(x: 0, y: 60)
context?.scaleBy(x: 1.0, y: -1.0)
//初始化字符串,字體,和上下文
let keys: [CFString] = [kCTFontAttributeName]
let keybuffer = buffer(to: CFString.self, source: keys)
let values: [CTFont] = [font]
let valueBuffer = buffer(to: CTFont.self, source: values)
var keyCallbacks = kCFTypeDictionaryKeyCallBacks
var valueCallbacks = kCFTypeDictionaryValueCallBacks
let attributes = CFDictionaryCreate(kCFAllocatorDefault, UnsafeMutablePointer<UnsafeRawPointer?>(keybuffer), UnsafeMutablePointer<UnsafeRawPointer?>(valueBuffer), 0, &keyCallbacks, &valueCallbacks)
let attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes)
let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
let components: Array<CGFloat> = [1.0,1.0, 1.0, 0.8]
let red = CGColor(colorSpace: rgbColorSpace, components: components)
CFAttributedStringSetAttribute((attrString as! CFMutableAttributedString), CFRangeMake(0, text.count), NSAttributedString.Key.foregroundColor as CFString, red)
let line = CTLineCreateWithAttributedString(attrString!)
context?.textPosition = CGPoint(x: 5, y: 50)
CTLineDraw(line, context!)
}
// UnsafeMutablePointer<UnsafeRawPointer?>類型數(shù)據(jù)創(chuàng)建
fileprivate func buffer<T>(to type:T.Type, source:[T]) -> UnsafeMutablePointer<UnsafeRawPointer?>{
let buffer = UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: source.count)
for idx in 0..<source.count {
let m_ptr = UnsafeMutableRawPointer.allocate(byteCount: MemoryLayout<T>.size, alignment: MemoryLayout<T>.alignment)
let bindptr = m_ptr.bindMemory(to: type, capacity: 1)
bindptr.initialize(to: source[idx])
let pty = UnsafeRawPointer(m_ptr)
buffer.advanced(by: idx).pointee = pty
}
return buffer
}
列式布局
在多列中布置文本是另一種常見的排版操作。嚴格來說,Core Text
本身一次只列出一列,不計算列大小或位置。您在調(diào)用Core Text
之前執(zhí)行這些操作,以在您計算的路徑區(qū)域內(nèi)布置文本。在此示例中,Core Text
除了在每列中布置文本外,還為每列提供了文本字符串中的子范圍。
清單2-3
中的createColumnsWithColumnCount:
方法接受要繪制的列數(shù)作為參數(shù),并返回路徑數(shù)組,每列一個路徑。
清單2-4
包含drawRect: 方法的實現(xiàn),該方法調(diào)用本清單中首先定義的本地createColumnsWithColumnCount
方法。此代碼駐留在UIView
子類(OS X
中的NSView
子類)中。子類包含一個attributesString
屬性,此屬性未在此處顯示,但在此列表中調(diào)用其訪問者以返回要布局的屬性字符串。
清單2-3
將視圖劃分為列
- (CFArrayRef)createColumnsWithColumnCount:(int)columnCount
{
int column;
CGRect* columnRects = (CGRect*)calloc(columnCount, sizeof(*columnRects));
// 設置第一列以覆蓋整個視圖。
columnRects[0] = self.bounds;
// 在框架的寬度上平均分配列。
CGFloat columnWidth = CGRectGetWidth(self.bounds) / columnCount;
for (column = 0; column < columnCount - 1; column++) {
CGRectDivide(columnRects[column], &columnRects[column],
&columnRects[column + 1], columnWidth, CGRectMinXEdge);
}
// 將所有列插入幾個像素的邊距。
for (column = 0; column < columnCount; column++) {
columnRects[column] = CGRectInset(columnRects[column], 8.0, 15.0);
}
// 創(chuàng)建一個布局路徑數(shù)組,每列一個。
CFMutableArrayRef array =
CFArrayCreateMutable(kCFAllocatorDefault,
columnCount, &kCFTypeArrayCallBacks);
for (column = 0; column < columnCount; column++) {
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, columnRects[column]);
CFArrayInsertValueAtIndex(array, column, path);
CFRelease(path);
}
free(columnRects);
return array;
}
清單2-4
執(zhí)行列式文本布局
// Override drawRect: 將屬性字符串繪制到列中。
// (在OS X中,NSView的drawRect:方法采用NSRect參數(shù),
// 但該列表中未使用該參數(shù)。)
- (void)drawRect:(CGRect)rect{
// 在iOS中初始化圖形上下文。
CGContextRef context = UIGraphicsGetCurrentContext();
// 僅在iOS中翻轉(zhuǎn)上下文坐標。
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// 在OS X中初始化圖形上下文是不同的:
// CGContextRef context =
// (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
// 設置文本矩陣。
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
// 使用屬性字符串創(chuàng)建框架集。
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(
(CFAttributedStringRef)self.attributedString);
// 調(diào)用createColumnsWithColumnCount函數(shù)來創(chuàng)建一個數(shù)組
// 三條路徑(列)。
CFArrayRef columnPaths = [self createColumnsWithColumnCount:3];
CFIndex pathCount = CFArrayGetCount(columnPaths);
CFIndex startIndex = 0;
int column;
// 為每列(路徑)創(chuàng)建一個框架。
for (column = 0; column < pathCount; column++) {
//獲取此列的路徑。
CGPathRef path = (CGPathRef)CFArrayGetValueAtIndex(columnPaths, column);
//為此列創(chuàng)建一個框架并繪制它。
CTFrameRef frame = CTFramesetterCreateFrame(
framesetter, CFRangeMake(startIndex, 0), path, NULL);
CTFrameDraw(frame, context);
//在此框架中不可見的第一個字符處開始下一幀。
CFRange frameRange = CTFrameGetVisibleStringRange(frame);
startIndex += frameRange.length;
CFRelease(frame);
}
CFRelease(columnPaths);
CFRelease(framesetter);
}
手動換行
在Core Text
中,除非您有特殊的連字過程或類似要求,否則通常不需要手動換行。framesetter
自動執(zhí)行換行。或者,Core Tex
t使你可以準確指定每行文本的中斷位置。清單2-5
顯示了如何創(chuàng)建排版工具,框架集使用的對象,以及直接使用排版工具查找適當?shù)膿Q行符并手動創(chuàng)建排版行。此示例還顯示了在繪制之前如何使線居中。
此代碼可以位于UIView
子類的drawRect: 方法中(OS X
中的NSView
子類)。該列表未顯示代碼中使用的變量的初始化。
清單2-5
執(zhí)行手動換行
double width; CGContextRef context; CGPoint textPosition; CFAttributedStringRef attrString;
// 初始化這些變量。
// 使用屬性字符串創(chuàng)建排字機。
CTTypesetterRef typesetter =
CTTypesetterCreateWithAttributedString(attrString);
// 找到從字符串開頭到給定寬度的行的中斷。
CFIndex start = 0;
CFIndex count = CTTypesetterSuggestLineBreak(typesetter, start, width);
// 使用返回的字符數(shù)(到中斷)來創(chuàng)建該行。
CTLineRef line = CTTypesetterCreateLine(typesetter, CFRangeMake(start, count));
// 獲得使線條居中所需的偏移量。
float flush = 0.5; // centered
double penOffset = CTLineGetPenOffsetForFlush(line, flush, width);
// 按計算的偏移量移動給定的文本繪圖位置并繪制線條。
CGContextSetTextPosition(context, textPosition.x + penOffset, textPosition.y);
CTLineDraw(line, context);
// 將索引移到換行符之外。
start += count;
下面是我自己的代碼(swift):
// 執(zhí)行手動換行
override func draw(_ rect: CGRect) {
super.draw(rect)
// 在iOS中初始化圖形上下文。
let context = UIGraphicsGetCurrentContext()
// 翻轉(zhuǎn)上下文坐標,僅限iOS。
context?.translateBy(x: 0, y: self.bounds.size.height)
context?.scaleBy(x: 1.0, y: -1.0)
// 設置文本矩陣。
context?.textMatrix = .identity
// 創(chuàng)建一個限定要繪制文本的區(qū)域的路徑。
// 路徑不需要一定是矩形。
let path = CGMutablePath()
// 在這個簡單的例子中,初始化一個矩形路徑。
let bounds = self.bounds
path.addRect(bounds)
// 初始化一個字符串。
let string = "Hello, World! I know nothing in the world that has as much power as a word. Sometimes I write one, and I look at it, until it begins to shine."
let textString: CFString = string as CFString
// 創(chuàng)建一個最大長度為0的可變屬性字符串。
// 最大長度是關(guān)于要保留多少內(nèi)部存儲空間的提示。
// 0表示沒有提示。
let attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0)
// 將textString復制到新創(chuàng)建的attrString中
CFAttributedStringReplaceString(attrString, CFRangeMake(0, 0), textString)
// 創(chuàng)建一個將作為屬性添加到attrString的顏色。
let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
let components: Array<CGFloat> = [1.0,1.0, 1.0, 0.8]
let white = CGColor(colorSpace: rgbColorSpace, components: components)
// 將字符的顏色設置為白色。
CFAttributedStringSetAttribute(attrString, CFRangeMake(0, string.count), NSAttributedString.Key.foregroundColor as CFString, white)
let width: Double = 150
let textPosition: CGPoint = CGPoint(x: 0, y: 200)
let font: CTFont = CTFontCreateWithName(textString, 22.0, nil)
CFAttributedStringSetAttribute(attrString, CFRangeMake(0, string.count), NSAttributedString.Key.font as CFString, font)
// 使用屬性字符串創(chuàng)建排字機。
let typesetter = CTTypesetterCreateWithAttributedString(attrString!)
// 找到從字符串開頭到給定寬度的行的中斷。
var start: CFIndex = 10
let count: CFIndex = CTTypesetterSuggestLineBreak(typesetter, start, width)
// 使用返回的字符數(shù)(到中斷)來創(chuàng)建該行。
let line = CTTypesetterCreateLine(typesetter, CFRangeMake(start, count))
// 獲得使線條居中所需的偏移量。
let flush: CGFloat = 0.5
let penOffset: CGFloat = CGFloat(CTLineGetPenOffsetForFlush(line, flush, width))
// 按計算的偏移量移動給定的文本繪圖位置并繪制線條。
context?.textPosition = CGPoint(x: textPosition.x + penOffset, y: textPosition.y)
CTLineDraw(line, context!)
start += count
}
// UnsafeMutablePointer<UnsafeRawPointer?>類型數(shù)據(jù)創(chuàng)建
fileprivate func buffer<T>(to type:T.Type, source:[T]) -> UnsafeMutablePointer<UnsafeRawPointer?>{
let buffer = UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: source.count)
for idx in 0..<source.count {
let m_ptr = UnsafeMutableRawPointer.allocate(byteCount: MemoryLayout<T>.size, alignment: MemoryLayout<T>.alignment)
let bindptr = m_ptr.bindMemory(to: type, capacity: 1)
bindptr.initialize(to: source[idx])
let pty = UnsafeRawPointer(m_ptr)
buffer.advanced(by: idx).pointee = pty
}
return buffer
}
應用段落樣式
清單2-6
實現(xiàn)了一個將段落樣式應用于屬性字符串的函數(shù)。該函數(shù)接受字體名稱,磅值和行間距作為參數(shù),這增加或減少文本行之間的空間量。此函數(shù)由清單2-7
中的代碼調(diào)用,該代碼創(chuàng)建純文本字符串,使用applyParaStyle
函數(shù)創(chuàng)建具有給定段落屬性的屬性字符串,然后創(chuàng)建frame
集和frame
,并繪制frame
。
清單2-6
應用段落樣式
NSAttributedString* applyParaStyle(
CFStringRef fontName , CGFloat pointSize,
NSString *plainText, CGFloat lineSpaceInc){
// 創(chuàng)建字體,以便我們確定其高度。
CTFontRef font = CTFontCreateWithName(fontName, pointSize, NULL);
// 設置行間距。
CGFloat lineSpacing = (CTFontGetLeading(font) + lineSpaceInc) * 2;
// 創(chuàng)建段落樣式設置。
CTParagraphStyleSetting setting;
setting.spec = kCTParagraphStyleSpecifierLineSpacing;
setting.valueSize = sizeof(CGFloat);
setting.value = &lineSpacing;
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(&setting, 1);
// 將段落樣式添加到字典中。
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)font, (id)kCTFontNameAttribute,
(__bridge id)paragraphStyle,
(id)kCTParagraphStyleAttributeName, nil];
CFRelease(font);
CFRelease(paragraphStyle);
// 將段落樣式應用于字符串以創(chuàng)建屬性字符串。
NSAttributedString* attrString = [[NSAttributedString alloc]
initWithString:(NSString*)plainText
attributes:attributes];
return attrString;
}
在清單2-7
中,樣式化字符串用于創(chuàng)建框架集。代碼使用框架集創(chuàng)建框架并繪制框架。
清單2-7
繪制樣式段落
- (void)drawRect:(CGRect)rect {
// 在iOS中初始化圖形上下文。
CGContextRef context = UIGraphicsGetCurrentContext();
// 僅在iOS中翻轉(zhuǎn)上下文坐標。
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// 設置文本矩陣。
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CFStringRef fontName = CFSTR("Didot Italic");
CGFloat pointSize = 24.0;
CFStringRef string = CFSTR("Hello, World! I know nothing in the world that has
as much power as a word. Sometimes I write one,
and I look at it, until it begins to shine.");
// 應用段落樣式。
NSAttributedString* attrString = applyParaStyle(fontName, pointSize, string, 50.0);
// 將帶有應用段落樣式的屬性字符串放入框架集中。
CTFramesetterRef framesetter =
CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrString);
// 創(chuàng)建一個填充視圖的路徑。
CGPathRef path = CGPathCreateWithRect(rect, NULL);
// 創(chuàng)建一個繪制框架。
CTFrameRef frame = CTFramesetterCreateFrame(
framesetter, CFRangeMake(0, 0), path, NULL);
// 繪制frame。
CTFrameDraw(frame, context);
CFRelease(frame);
CGPathRelease(path);
CFRelease(framesetter);
}
在OS X
中,NSView
drawRect:方法接收NSRect參數(shù),但CGPathCreateWithRect 函數(shù)需要CGRect
參數(shù)。因此,您必須使用以下函數(shù)調(diào)用將NSRect
對象轉(zhuǎn)換為CGRect
對象:
CGRect myRect = NSRectToCGRect([self bounds]);
此外,在OS X
中,您可以獲得不同的圖形上下文,并且不會翻轉(zhuǎn)其坐標,如清單2-7
中的注釋所示。
在非矩形區(qū)域中顯示文本
在非矩形區(qū)域中顯示文本的困難部分是描述非矩形路徑。清單2-8
中的AddSquashedDonutPath
函數(shù)返回一個環(huán)形路徑。獲得路徑后,只需調(diào)用常用的Core Text
函數(shù)即可應用屬性并繪制。
清單2-8
在非矩形路徑中顯示文本
// 創(chuàng)建一個甜甜圈形狀的路徑。
static void AddSquashedDonutPath(CGMutablePathRef path,
const CGAffineTransform *m, CGRect rect)
{
CGFloat width = CGRectGetWidth(rect);
CGFloat height = CGRectGetHeight(rect);
CGFloat radiusH = width / 3.0;
CGFloat radiusV = height / 3.0;
CGPathMoveToPoint( path, m, rect.origin.x, rect.origin.y + height - radiusV);
CGPathAddQuadCurveToPoint( path, m, rect.origin.x, rect.origin.y + height,
rect.origin.x + radiusH, rect.origin.y + height);
CGPathAddLineToPoint( path, m, rect.origin.x + width - radiusH,
rect.origin.y + height);
CGPathAddQuadCurveToPoint( path, m, rect.origin.x + width,
rect.origin.y + height,
rect.origin.x + width,
rect.origin.y + height - radiusV);
CGPathAddLineToPoint( path, m, rect.origin.x + width,
rect.origin.y + radiusV);
CGPathAddQuadCurveToPoint( path, m, rect.origin.x + width, rect.origin.y,
rect.origin.x + width - radiusH, rect.origin.y);
CGPathAddLineToPoint( path, m, rect.origin.x + radiusH, rect.origin.y);
CGPathAddQuadCurveToPoint( path, m, rect.origin.x, rect.origin.y,
rect.origin.x, rect.origin.y + radiusV);
CGPathCloseSubpath( path);
CGPathAddEllipseInRect( path, m,
CGRectMake( rect.origin.x + width / 2.0 - width / 5.0,
rect.origin.y + height / 2.0 - height / 5.0,
width / 5.0 * 2.0, height / 5.0 * 2.0));
}
// 生成drawRect調(diào)用之外的路徑,以便僅計算路徑一次。
- (NSArray *)paths{
CGMutablePathRef path = CGPathCreateMutable();
CGRect bounds = self.bounds;
bounds = CGRectInset(bounds, 10.0, 10.0);
AddSquashedDonutPath(path, NULL, bounds);
NSMutableArray *result =
[NSMutableArray
arrayWithObject:CFBridgingRelease(path)];
return result;
}
- (void)drawRect:(CGRect)rect{
[super drawRect:rect];
// 在iOS中初始化圖形上下文。
CGContextRef context = UIGraphicsGetCurrentContext();
// 僅在iOS中翻轉(zhuǎn)上下文坐標。
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// 設置文本矩陣。
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
// 初始化屬性字符串。
CFStringRef textString = CFSTR("Hello, World! I know nothing in the world that
has as much power as a word. Sometimes I write one, and I look at it,
until it begins to shine.");
// 創(chuàng)建一個可變的屬性字符串。
CFMutableAttributedStringRef attrString =
CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
// 將textString復制到新創(chuàng)建的attrString中。
CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0), textString);
// 創(chuàng)建一個將作為屬性添加到attrString的顏色。
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat components[] = { 1.0, 0.0, 0.0, 0.8 };
CGColorRef red = CGColorCreate(rgbColorSpace, components);
CGColorSpaceRelease(rgbColorSpace);
// 將前13個字符的顏色設置為紅色。
CFAttributedStringSetAttribute(attrString, CFRangeMake(0, 13),
kCTForegroundColorAttributeName, red);
// 使用屬性字符串創(chuàng)建框架集。
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
// 創(chuàng)建用于繪制文本的路徑數(shù)組。
NSArray *paths = [self paths];
CFIndex startIndex = 0;
// 在OS X中,使用NSColor而不是UIColor。
#define GREEN_COLOR [UIColor greenColor]
#define YELLOW_COLOR [UIColor yellowColor]
#define BLACK_COLOR [UIColor blackColor]
// 對于路徑數(shù)組中的每個路徑......
for (id object in paths) {
CGPathRef path = (__bridge CGPathRef)object;
// 將路徑的背景設置為黃色。
CGContextSetFillColorWithColor(context, [YELLOW_COLOR CGColor]);
CGContextAddPath(context, path);
CGContextFillPath(context);
CGContextDrawPath(context, kCGPathStroke);
// 為此路徑創(chuàng)建frame并繪制文本。
CTFrameRef frame = CTFramesetterCreateFrame(framesetter,
CFRangeMake(startIndex, 0), path, NULL);
CTFrameDraw(frame, context);
// 在此frame中不可見的第一個字符處開始下一幀。
CFRange frameRange = CTFrameGetVisibleStringRange(frame);
startIndex += frameRange.length;
CFRelease(frame);
}
CFRelease(attrString);
CFRelease(framesetter);
}
上一章 | 目錄 | 下一章 |
---|