農林漁牧網

您現在的位置是:首頁 > 林業

在iOS中繪圖和列印

2022-02-11由 小趙學程式設計 發表于 林業

▇ios怎麼打出來黑方塊

在iOS中繪圖和列印

本檔案涵蓋三個相關主題:

繪製自定義UI檢視。自定義UI檢視允許您繪製標準UI元素難以繪製的內容。例如,繪圖程式可能使用使用者繪圖的自定義檢視,或者街機遊戲可能使用繪製精靈的自定義檢視。

繪製成螢幕外的點陣圖和PDF內容。不管您是打算稍後顯示影象、將它們匯出到檔案中,還是將影象列印到支援airprint的印表機中,offscreen繪圖都允許您這樣做,而不會中斷使用者的工作流程。

在你的應用程式中新增AirPrint支援。iOS列印系統可以讓你繪製不同的內容以適應頁面。

iOS原生圖形系統結合了三種主要技術:UIKit、Core graphics和Core Animation。UIKit在那些檢視中提供了檢視和一些高階的繪圖功能,Core Graphics在UIKit檢視中提供了額外的(低階的)繪圖支援,Core Animation提供了將轉換和動畫應用到UIKit檢視的能力。Core Animation也負責檢視合成。

自定義UI檢視允許更大的繪圖靈活性

本文件描述瞭如何使用本地繪圖技術繪製自定義UI檢視。這些技術,包括核心圖形和UIKit框架,支援2D繪圖。

在考慮使用自定義UI檢視之前,您應該確保確實需要這樣做。本機繪圖適合處理更復雜的2D佈局需求。但是,由於自定義檢視是處理器密集型的,因此應該限制使用本機繪圖技術繪製的數量。

作為自定義繪圖的另一種選擇,iOS應用程式可以透過其他幾種方式在螢幕上繪圖。

使用標準(內建)檢視。標準檢視允許您繪製常見的使用者介面原語,包括列表、集合、警報、影象、進度條、表等等,而不需要您自己顯式地繪製任何東西。使用內建檢視不僅可以確保iOS應用程式之間的使用者體驗一致,還可以節省程式設計工作。如果內建檢視滿足你的需要,你應該閱讀iOS的檢視程式設計指南。

使用核心動畫層。Core Animation允許你用動畫和轉換來建立複雜的、分層的2D檢視。Core Animation是一個很好的選擇,可以用來製作標準檢視的動畫,或者用複雜的方式組合檢視來呈現深度的錯覺,並且可以像本文件中描述的那樣與定製繪製的檢視相結合。要了解更多關於核心動畫,請閱讀核心動畫概述。

在GLKit檢視或自定義檢視中使用OpenGL ES。OpenGL ES框架提供了一套開放標準的圖形庫,主要用於遊戲開發或需要高幀率的應用程式,如虛擬原型應用程式、機械和架構設計應用程式。它符合OpenGL ES 2。0和OpenGL ES v1。1規範。要了解更多關於OpenGL繪圖,請閱讀OpenGL ES程式設計指南。

使用web內容。UIWebView類允許你在iOS應用程式中顯示基於web的使用者介面。要學習如何在web檢視中顯示web內容,請使用UIWebView來顯示選擇的文件型別和UIWebView類引用。

根據您建立的應用程式的型別,可以使用少量或不使用自定義繪圖程式碼。雖然沉浸式應用程式通常會大量使用自定義繪圖程式碼,但實用程式和生產力應用程式通常可以使用標準檢視和控制元件來顯示其內容。

自定義繪圖程式碼的使用應該限制在需要動態更改顯示內容的情況下。例如,繪圖應用程式通常需要使用自定義繪圖程式碼來跟蹤使用者的繪圖命令,而arcade風格的遊戲可能需要不斷更新螢幕以反映不斷變化的遊戲環境。在這些情況下,您應該選擇適當的繪圖技術,並建立一個自定義檢視類來處理事件並適當地更新顯示。

另一方面,如果你的應用程式的大部分介面是固定的,你可以提前渲染介面到一個或多個影象檔案,並在執行時使用UIImageView類顯示這些影象。您可以根據需要將影象檢視與其他內容分層以構建您的介面。您還可以使用UILabel類來顯示可配置的文字,幷包含按鈕或其他控制元件來提供互動性。例如,一個電子版本的棋盤遊戲通常可以用很少或沒有自定義繪圖程式碼來建立。

因為自定義檢視通常需要更多的處理器(GPU的幫助更少),如果你可以使用標準檢視來做你需要做的事情,你應該一直這樣做。此外,您應該使您的自定義檢視儘可能小,只包含您不能以任何其他方式繪製的內容,使用使用標準檢視的一切。如果您需要將標準UI元素與自定義繪圖相結合,請考慮使用Core Animation層將自定義檢視與標準檢視疊加在一起,以便儘可能少地繪圖。

一些關鍵概念支援使用本地技術進行繪圖

當你用UIKit和核心圖形繪製內容時,除了檢視繪製週期外,你應該熟悉一些概念。

對於drawRect:方法,UIKit建立一個圖形上下文來呈現給顯示。這個圖形上下文包含繪圖系統執行繪圖命令所需的資訊,包括諸如填充和描邊顏色、字型、裁剪區域和行寬等屬性。您還可以為點陣圖影象和PDF內容建立和繪製自定義圖形上下文。

UIKit有一個預設的座標系統繪圖的原點在檢視的左上角;正值向下延伸到原點的右邊。透過修改當前轉換矩陣,您可以更改相對於底層檢視或視窗的預設座標系統的大小、方向和位置,該轉換矩陣將檢視的座標空間對映到裝置螢幕。

在iOS中,邏輯座標空間(以點為單位度量距離)並不等於裝置座標空間(以畫素為單位度量距離)。為了獲得更高的精度,點用浮點值表示。

UIKit, Core Graphics,和Core Animation給你的應用很多繪圖工具

UIKit和Core Graphics有許多互補的圖形功能,包括圖形上下文、Bezier路徑、影象、點陣圖、透明層、顏色、字型、PDF內容,以及繪製矩形和剪下區域。此外,核心圖形還具有與線屬性、顏色空間、模式顏色、漸變、陰影和影象遮罩相關的功能。核心動畫框架允許您透過操作和顯示使用其他技術建立的內容來建立流暢的動畫。

相關章節:iOS繪圖概念,使用Bezier路徑繪製圖形,繪圖和建立影象,生成PDF內容

應用程式可以畫成螢幕外的點陣圖或pdf

應用程式在螢幕外繪製內容通常很有用:

當按比例縮小照片以供上傳、將內容呈現到影象檔案以供儲存或使用核心圖形生成複雜影象以供顯示時,經常使用螢幕外的點陣圖上下文。

當繪製使用者生成的內容用於列印時,通常使用螢幕外的PDF上下文。

在建立了一個螢幕外的上下文之後,您可以像在自定義檢視的drawRect:方法中繪製一樣在其中繪製。

應用程式有一系列列印內容的選項

從ios4。2開始,應用程式可以使用AirPrint將內容無線列印到支援的印表機上。當裝配一個列印作業時,他們有三種方法給UIKit要列印的內容:

它們可以為框架提供一個或多個可直接列印的物件;這些物件只需要很少的應用程式參與。這些是包含或引用影象資料或PDF內容的NSData、NSURL、UIImage或ALAsset類的例項。

它們可以將列印格式化程式分配給列印作業。列印格式化程式是一個物件,它可以在多個頁面上顯示某種型別的內容(如純文字或HTML)。

他們可以為列印作業分配一個頁面渲染器。頁面渲染器通常是UIPrintPageRenderer的自定義子類的一個例項,它繪製要部分或全部列印的內容。頁面呈現程式可以使用一個或多個列印格式化程式來幫助繪製和格式化其可列印內容。

為高解析度螢幕更新應用程式很容易

一些iOS裝置具有高解析度螢幕,因此你的應用程式必須準備好在這些裝置上執行,以及在低解析度螢幕上執行。iOS處理了很多需要處理不同解析度的工作,但你的應用程式必須完成其餘的工作。您的任務包括提供專門命名的高解析度影象,並修改與層和影象相關的程式碼以考慮當前的比例因子。

iOS圖概念

高質量的圖形是應用程式使用者介面的重要組成部分。提供高質量的圖形不僅讓你的應用程式看起來很好,而且也讓你的應用程式看起來像系統其餘部分的自然擴充套件。iOS為在你的系統中建立高質量的圖形提供了兩條主要路徑:OpenGL或使用Quartz、Core Animation和UIKit的原生渲染。本文件描述了本機呈現。(有關OpenGL繪圖,請參閱OpenGL ES程式設計指南。)

Quartz是主要的繪圖介面,它支援基於路徑的繪圖、抗鋸齒的呈現、漸變填充模式、影象、顏色、座標空間轉換以及PDF文件的建立、顯示和解析。UIKit提供了Objective-C包裝器,用於線條、石英影象和色彩操作。Core Animation為改變許多UIKit檢視屬性提供了底層支援,也可以用來實現自定義動畫。

本章概述了iOS應用程式的繪圖過程,以及每種支援的繪圖技術的具體繪圖技術。你還可以找到關於如何最佳化iOS平臺的繪圖程式碼的提示和指導。

UIKit圖形系統

在iOS中,所有的繪圖到螢幕——不管它是否涉及OpenGL, Quartz, UIKit,或核心動畫——都發生在UIView類或其子類的一個例項範圍內。檢視定義發生繪圖的螢幕部分。如果您使用系統提供的檢視,此繪圖將自動為您處理。但是,如果定義自定義檢視,則必須自己提供繪圖程式碼。如果您使用Quartz、Core Animation和UIKit來繪圖,那麼您將使用以下部分中描述的繪圖概念。

除了直接在螢幕上繪圖外,UIKit還允許你在螢幕外的點陣圖和PDF圖形上下文中繪圖。當您在螢幕外的上下文中繪製時,您不是在檢視中繪製,這意味著諸如檢視繪製週期之類的概念並不適用(除非您隨後獲得該影象並將其繪製到影象檢視或類似的檢視中)。

檢視繪製週期

UIView類子類的基本繪圖模型涉及到按需更新內容。UIView類使更新過程更簡單和更有效;但是,透過收集您發出的更新請求,並在最適當的時候將它們交付給繪圖程式碼。

當檢視首次顯示或檢視的一部分需要重新繪製時,iOS會透過呼叫檢視的drawRect:方法來要求檢視繪製其內容。

有幾個動作可以觸發檢視更新:

移動或刪除另一個部分模糊了您的檢視的檢視

將先前隱藏的檢視的隱藏屬性設定為NO,使其再次可見

從螢幕上滾動檢視,然後回到螢幕上

顯式呼叫檢視的setNeedsDisplay或setNeedsDisplayInRect:方法

系統檢視將自動重新繪製。對於自定義檢視,必須重寫drawRect:方法並在其中執行所有繪圖。在drawRect:方法中,使用本地繪圖技術來繪製形狀、文字、影象、漸變或任何其他您想要的可視內容。當檢視第一次可見時,iOS會將一個矩形傳遞給檢視的drawRect:方法,該方法包含檢視的整個可見區域。在隨後的呼叫中,矩形只包含檢視中需要重繪的部分。為了獲得最大的效能,您應該只重新繪製受影響的內容。

呼叫drawRect:方法後,檢視將自身標記為已更新,並等待新操作到達並觸發另一個更新週期。如果您的檢視顯示靜態內容,那麼您所需要做的就是響應由於滾動和其他檢視的出現而導致的檢視可見性的變化。

然而,如果你想要改變檢視的內容,你必須告訴你的檢視重新繪製它的內容。為此,請呼叫setNeedsDisplay或setNeedsDisplayInRect:方法來觸發更新。例如,如果您每秒更新內容幾次,您可能需要設定一個計時器來更新檢視。您還可以根據使用者互動或檢視中新內容的建立來更新檢視。

重要的是:不要自己呼叫檢視的drawRect:方法。該方法只能在螢幕重繪期間由iOS內建的程式碼呼叫。在其他時候,沒有圖形上下文存在,所以繪圖是不可能的。(圖形上下文將在下一節中解釋。)

iOS中的座標系統和繪圖

當應用程式在iOS中繪製內容時,它必須在一個由座標系統定義的二維空間中定位所繪製的內容。乍一看,這個概念似乎很簡單,但事實並非如此。iOS中的應用程式在繪圖時有時需要處理不同的座標系統。

在iOS中,所有的繪圖都發生在一個圖形上下文中。從概念上講,圖形上下文是一個物件,它描述繪圖的位置和方式,包括基本的繪圖屬性,如繪圖時使用的顏色、裁剪區域、線寬和樣式資訊、字型資訊、合成選項等。

另外,如圖1-1所示,每個圖形上下文都有一個座標系統。更準確地說,每個圖形上下文有三個座標系統:

繪圖(使用者)座標系。當您發出繪圖命令時,將使用此座標系統。

檢視座標系(基本空間)。這個座標系是相對於檢視的固定座標系。

(物理)裝置座標系。這個座標系統表示物理螢幕上的畫素。

在iOS中,UIScreen、UIView、UIImage和CALayer類提供屬性來獲取(在某些情況下,還提供set)一個比例因子,用於描述特定物件的點和畫素之間的關係。例如,每個UIKit檢視都有一個contentScaleFactor屬性。在標準解析度螢幕上,比例係數通常為1。0。在高解析度螢幕上,比例因子通常是2。0。在未來,其他規模因素也可能成為可能。(在版本4之前的iOS中,你應該假設比例係數為1。0。)

本地繪圖技術(如Core Graphics)會考慮到當前的比例因素。例如,如果你的一個檢視實現了一個drawRect:方法,UIKit會自動將檢視的縮放因子設定為螢幕的縮放因子。此外,UIKit會自動修改當前的轉換矩陣,在繪製過程中使用的任何圖形上下文都會考慮到檢視的縮放因子。因此,在drawRect:方法中繪製的任何內容都會根據底層裝置的螢幕進行適當的縮放。

由於這種自動對映,在編寫繪圖程式碼時,畫素通常無關緊要。然而,有時你可能需要根據點如何對映到畫素來改變你的應用程式的繪圖行為——例如,在具有高解析度螢幕的裝置上下載高解析度影象,或者在低解析度螢幕上繪圖時避免縮放工件。

在iOS中,當你在螢幕上畫東西時,圖形子系統使用一種叫做反鋸齒的技術來在低解析度的螢幕上近似高解析度的影象。解釋這一技術的最佳方法是舉例說明。當你在純白色背景上畫一條黑色的垂直線時,如果這條直線正好落在一個畫素上,它就會以白色區域中的一系列黑色畫素的形式出現。但是,如果它恰好出現在兩個畫素之間,則顯示為兩個並排的灰色畫素。

獲取圖形上下文

大多數情況下,圖形上下文都是為您配置的。每個檢視物件都會自動建立一個圖形上下文,以便在呼叫自定義drawRect:方法時,您的程式碼可以立即開始繪圖。作為這個配置的一部分,底層的UIView類為當前的繪圖環境建立一個圖形上下文(一個CGContextRef不透明型別)。

如果您想在檢視之外的其他地方繪圖(例如,在PDF或點陣圖檔案中捕獲一系列繪圖操作),或者需要呼叫需要上下文物件的核心圖形函式,則必須採取其他步驟來獲取圖形上下文物件。

在螢幕上繪圖

如果您使用核心圖形函式來繪製檢視,無論是在drawRect:方法中還是在其他地方,您都需要一個繪圖上下文。(許多函式的第一個引數必須是CGContextRef物件。)你可以呼叫函式UIGraphicsGetCurrentContext來獲得在drawRect:中隱式生成的同一個圖形上下文的顯式版本。因為它是相同的圖形上下文,所以繪圖函式也應該引用ULO預設座標系統。

如果你想使用核心圖形函式在UIKit檢視中繪圖,你應該使用UIKit的ULO座標系統來進行繪圖操作。或者,你可以對CTM應用一個翻轉變換,然後在UIKit檢視中使用Core Graphics原生LLO座標系統繪製一個物件。翻轉預設座標系統將詳細討論翻轉轉換。

UIGraphicsGetCurrentContext函式總是返回當前有效的圖形上下文。例如,如果你建立一個PDF上下文,然後呼叫UIGraphicsGetCurrentContext,你會收到那個PDF上下文。你必須使用UIGraphicsGetCurrentContext返回的圖形上下文,如果你使用核心圖形函式來繪製檢視。

使用Quartz和UIKit繪圖

Quartz是iOS中原生繪圖技術的總稱。核心圖形框架位於Quartz的核心,是用於繪製內容的主要介面。這個框架提供了資料型別和函式來操作以下內容:

圖形上下文

路徑

圖片和點陣圖

透明層

顏色、圖案顏色和顏色空間

梯度和陰影

字型

PDF內容

UIKit基於Quartz的基本特性,為圖形相關的操作提供了一組集中的類。UIKit圖形類並不打算作為一套全面的繪圖工具——核心圖形已經提供了。相反,它們為其他UIKit類提供繪圖支援。UIKit支援包括以下類和函式:

UIImage,它實現了一個用於顯示影象的不可變類

UIColor,它提供了對裝置顏色的基本支援

UIFont,它為需要它的類提供字型資訊

UIScreen,它提供關於螢幕的基本資訊

UIBezierPath,它能讓你的應用畫線,弧,橢圓,和其他形狀。

用於生成UIImage物件的JPEG或PNG表示的函式

用於繪製點陣圖圖形上下文的函式

透過將資料繪製到PDF圖形上下文中來生成PDF資料的函式

用於繪製矩形和剪下繪圖區域的函式

用於更改和獲取當前圖形上下文的函式

配置圖形上下文

在呼叫drawRect:方法之前,檢視物件建立一個圖形上下文並將其設定為當前上下文。此上下文僅在drawRect:呼叫的生命週期記憶體在。你可以透過呼叫UIGraphicsGetCurrentContext函式來獲取一個指向這個圖形上下文的指標。此函式返回對CGContextRef型別的引用,將其傳遞給核心圖形函式以修改當前圖形狀態。表1-1列出了用於設定圖形狀態的不同方面的主要函式。

圖形上下文包含儲存的圖形狀態的堆疊。當Quartz建立圖形上下文時,堆疊是空的。使用CGContextSaveGState函式將當前圖形狀態的副本推入堆疊。此後,對圖形狀態所做的修改將影響後續的繪圖操作,但不會影響儲存在堆疊上的副本。完成修改後,可以使用CGContextRestoreGState函式將儲存的狀態從堆疊頂部取出,從而返回到以前的圖形狀態。以這種方式推送和彈出圖形狀態是一種快速返回到以前狀態的方法,並且消除了單獨撤銷每個狀態更改的需要。這也是恢復狀態的某些方面(如裁剪路徑)到原始設定的唯一方法。

建立和繪製路徑

路徑是由一系列線條和貝塞爾曲線建立的基於向量的形狀。UIKit包括UIRectFrame和UIRectFill函式(還有其他的)用於在檢視中繪製簡單的路徑,例如矩形。核心圖形還包括用於建立簡單路徑(如矩形和橢圓)的方便函式。

對於更復雜的路徑,您必須使用UIKit的UIBezierPath類建立路徑,或者使用核心圖形框架中對CGPathRef不透明型別進行操作的函式。儘管您可以使用這兩種API來構造一個沒有圖形上下文的路徑,但是路徑中的點仍然必須引用當前的座標系統(它要麼具有ULO方向,要麼具有LLO方向),並且您仍然需要一個圖形上下文來實際呈現路徑。

在繪製路徑時,必須設定當前上下文。該上下文可以是自定義檢視的上下文(在drawRect:中)、點陣圖上下文或PDF上下文。座標系統決定了如何渲染路徑。UIBezierPath採用ULO座標系。因此,如果您的檢視被翻轉(使用LLO座標),結果形狀的呈現可能與預期的不同。為了獲得最好的結果,您應該始終指定相對於用於呈現的圖形上下文的當前座標系統的原點的點。

翻轉預設座標系統

在UIKit中翻轉繪圖,修改背景CALayer,使具有LLO座標系的繪圖環境與UIKit的預設座標系對齊。如果你只使用UIKit方法和函式來繪圖,你不需要翻轉CTM。然而,如果你混合了核心圖形或影象I/O函式呼叫和UIKit呼叫,翻轉CTM可能是必要的。

具體來說,如果您透過直接呼叫核心圖形函式來繪製圖像或PDF文件,則物件將在檢視的上下文中呈現。您必須翻轉CTM才能正確顯示影象和頁面。

要將繪製的物件翻轉到核心圖形上下文,使其在UIKit檢視中正確顯示,您必須分兩步修改CTM。將原點平移到繪圖區域的左上角,然後應用縮放平移,將y座標修改為-1。這樣做的程式碼看起來類似如下:

CGContextSaveGState(graphicsContext);

CGContextTranslateCTM(graphicsContext, 0。0, imageHeight);

CGContextScaleCTM(graphicsContext, 1。0, -1。0);

CGContextDrawImage(graphicsContext, image, CGRectMake(0, 0, imageWidth, imageHeight));

CGContextRestoreGState(graphicsContext);

如果你用一個核心圖形影象物件初始化建立一個UIImage物件,UIKit會為你執行翻轉轉換。每個UIImage物件背後都有一個CGImageRef不透明型別。您可以透過CGImage屬性訪問核心圖形物件,並對影象進行一些處理。(Core Graphics擁有UIKit中沒有的與影象相關的功能。)完成後,您可以從修改後的CGImageRef物件中重新建立UIImage物件。

在核心動畫層中考慮比例因素

直接使用核心動畫層來提供內容的應用程式可能需要調整其繪圖程式碼來考慮比例因素。通常,當你在檢視的drawRect:方法中繪製,或者在該層委託的drawLayer:inContext:方法中繪製時,系統會根據比例因素自動調整圖形上下文。然而,當您的檢視執行下列操作之一時,瞭解或更改該比例因子可能仍然是必要的:

使用不同的比例因子建立額外的核心動畫層,並將它們組合成自己的內容

直接設定核心動畫層的內容屬性

Core Animation的合成引擎會檢視每一層的contentsScale屬性,以決定在合成過程中該層的內容是否需要縮放。如果你的應用建立的層沒有關聯的檢視,每個新層物件的比例係數最初設定為1。0。如果不改變比例因子,並且隨後在高解析度螢幕上繪製該層,則該層的內容將自動縮放,以補償比例因子的差異。如果你不希望內容被縮放,你可以透過為contentsScale屬性設定一個新值來改變圖層的縮放因子為2。0,但是如果你這樣做而不提供高解析度的內容,你現有的內容可能會比你預期的要小。要解決這個問題,您需要為您的層提供更高解析度的內容。

重要提示:該層的contentsGravity屬性在決定標準解析度層內容是否在高解析度螢幕上縮放時起作用。預設情況下,該屬性設定為kCAGravityResize值,這將導致對層內容進行縮放以適應層的邊界。將gravity更改為nonresizing選項可以消除否則會發生的自動縮放。在這種情況下,您可能需要相應地調整內容或比例因子。

當你直接設定一個圖層的contents屬性時,調整圖層的內容以適應不同的比例因子是最合適的。石英影象沒有尺度因子的概念,因此可以直接使用畫素。因此,在建立您計劃用於該層內容的CGImageRef物件之前,請檢查比例因子並相應地調整影象的大小。具體來說,從你的應用程式包中載入一個適當大小的影象,或者使用UIGraphicsBeginImageContextWithOptions函式來建立一個影象,它的比例因子與你的圖層的比例因子匹配。如果您沒有建立高解析度的點陣圖,則現有的點陣圖可能會按前面討論的那樣縮放。

使用Bezier路徑繪製形狀

在ios3。2及以後的版本中,你可以使用UIBezierPath類來建立基於向量的路徑。UIBezierPath類是一個Objective-C包裝器,用於核心圖形框架中與路徑相關的特性。您可以使用這個類來定義簡單的形狀,例如橢圓和矩形,以及包含多個直線和曲線段的複雜形狀。

你可以使用路徑物件在你的應用程式的使用者介面中繪製形狀。你可以畫出路徑的輪廓,填充它所覆蓋的空間,或者兩者兼而有之。您還可以使用路徑來定義當前圖形上下文的裁剪區域,然後可以使用該區域來修改該上下文中的後續繪圖操作。

貝塞爾曲線路徑基礎知識

UIBezierPath物件是CGPathRef資料型別的包裝器。路徑是基於向量的形狀,使用直線和曲線段構建。您可以使用線段來建立矩形和多邊形,也可以使用曲線段來建立弧、圓和複雜的曲線形狀。每個段由一個或多個點(在當前座標系統中)和一個繪圖命令組成,該命令定義如何解釋這些點。

每一組連線的線段和曲線段構成了所謂的子路徑。子路徑中一個線段或曲線段的結束定義下一個線段的開始。單個UIBezierPath物件可能包含一個或多個定義整體路徑的子路徑,它們被moveToPoint:命令分隔開,這個命令可以有效地抬起繪圖筆,並將其移動到一個新的位置。

構建和使用path物件的過程是獨立的。建立路徑是第一個過程,包括以下步驟:

建立path物件。

設定UIBezierPath物件的任何相關繪圖屬性,比如描邊路徑的lineWidth或lineJoinStyle屬性,或者填充路徑的usesEvenOddFillRule屬性。這些繪圖屬性應用於整個路徑。

使用moveToPoint:方法設定初始段的起始點。

新增線段和曲線段來定義子路徑。

可選地,透過呼叫closePath來關閉子路徑,closePath將從最後一個段的末尾繪製到第一個段的開頭的直線段。

可以選擇重複步驟3、4和5來定義額外的子路徑。

在建立路徑時,應該將路徑上的點相對於原點(0,0)進行排列。這樣做可以使以後移動路徑更容易。在繪圖期間,路徑的點按原樣應用於當前圖形上下文的座標系統。如果您的路徑是相對於原點定向的,那麼您所要做的就是對當前圖形上下文應用帶有翻譯因子的仿射變換來重新定位它。修改圖形上下文(相對於path物件本身)的好處是,您可以透過儲存和恢復圖形狀態輕鬆地撤消轉換。

要繪製path物件,可以使用描邊和填充方法。這些方法在當前圖形上下文中呈現路徑的直線和曲線段。渲染過程包括使用path物件的屬性對線段和曲線段進行柵格化。柵格化過程不修改path物件本身。因此,您可以在當前上下文中或另一個上下文中多次呈現相同的path物件。

新增線條和多邊形到您的路徑

線條和多邊形是使用moveToPoint:和addLineToPoint:方法逐點構建的簡單形狀。方法設定要建立的形狀的起始點。然後,使用addLineToPoint:方法建立形狀的線條。您將連續建立這些行,其中每一行都是在您指定的前一個點和新點之間形成的。

清單2-1顯示了使用單獨的線段建立五邊形所需的程式碼。(圖2-1顯示了使用適當的筆畫和填充顏色設定繪製此形狀的結果,如渲染Bezier Path物件的內容所述。)該程式碼設定形狀的初始點,然後新增四個連線的線段。透過呼叫closePath方法新增第五個段,該方法連線最後一個點(0,40)和第一個點(100,0)。

建立一個五邊形:

UIBezierPath *aPath = [UIBezierPath bezierPath];

// Set the starting point of the shape。

[aPath moveToPoint:CGPointMake(100。0, 0。0)];

// Draw the lines。

[aPath addLineToPoint:CGPointMake(200。0, 40。0)];

[aPath addLineToPoint:CGPointMake(160, 140)];

[aPath addLineToPoint:CGPointMake(40。0, 140)];

[aPath addLineToPoint:CGPointMake(0。0, 40。0)];

[aPath closePath];

使用closePath方法不僅結束描述形狀的子路徑,它還在第一個點和最後一個點之間繪製了一條線段。這是一個方便的方式來完成一個多邊形,而不必畫最後的線。

建立一個新的弧路徑

// pi is approximately equal to 3。14159265359。

#define DEGREES_TO_RADIANS(degrees) ((pi * degrees)/ 180)

- (UIBezierPath *)createArcPath

{

UIBezierPath *aPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 150)

radius:75

startAngle:0

endAngle:DEGREES_TO_RADIANS(135)

clockwise:YES];

return aPath;

}

要在路徑上新增曲線,可以使用以下方法:

三次曲線:addCurveToPoint: controlPoint1: controlPoint2:

二次曲線:addQuadCurveToPoint: controlPoint:

因為曲線依賴於路徑的當前點,所以必須在呼叫前面的方法之前設定當前點。完成曲線後,當前點將更新到您指定的新端點。

建立橢圓和矩形路徑

橢圓和矩形是由曲線和線段組合而成的常見路徑型別。UIBezierPath類包括bezierPathWithRect:和bezierPathWithOvalInRect:建立橢圓或矩形路徑的方便方法。這兩個方法都建立一個新的path物件,並用指定的形狀初始化它。您可以立即使用返回的path物件,或者根據需要向其新增更多形狀。

如果您想要向現有的path物件新增一個矩形,您必須使用moveToPoint:、addLineToPoint:和closePath方法,就像您對任何其他多邊形所做的那樣。將closePath方法用於矩形的最終邊是一種方便的方法,可以新增路徑的最後一行並標記矩形子路徑的結束。

如果要在現有路徑中新增橢圓,最簡單的方法是使用核心圖形。雖然您可以使用addQuadCurveToPoint:controlPoint:來近似一個橢圓形表面,但CGPathAddEllipseInRect函式使用起來要簡單得多,而且更精確。有關更多資訊,請參見使用核心圖形函式修改路徑。

使用核心圖形函式修改路徑

UIBezierPath類實際上只是CGPathRef資料型別和與該路徑相關的繪圖屬性的包裝。雖然您通常使用UIBezierPath類的方法新增線段和曲線段,但該類也公開一個CGPath屬性,您可以使用它直接修改底層路徑資料型別。當您希望使用核心圖形框架的函式構建路徑時,可以使用此屬性。

有兩種方法來修改與UIBezierPath物件相關聯的路徑。你可以完全使用核心圖形函式來修改路徑,或者你可以混合使用核心圖形函式和UIBezierPath方法。在某些方面,完全使用核心圖形呼叫修改路徑更容易。您可以建立一個可變的CGPathRef資料型別,並呼叫任何需要修改其路徑資訊的函式。完成後,將path物件分配給對應的UIBezierPath物件,如清單2-3所示。

將一個新的CGPathRef賦值給一個UIBezierPath物件

// Create the path data。

CGMutablePathRef cgPath = CGPathCreateMutable();

CGPathAddEllipseInRect(cgPath, NULL, CGRectMake(0, 0, 300, 300));

CGPathAddEllipseInRect(cgPath, NULL, CGRectMake(50, 50, 200, 200));

// Now create the UIBezierPath object。

UIBezierPath *aPath = [UIBezierPath bezierPath];

aPath。CGPath = cgPath;

aPath。usesEvenOddFillRule = YES;

// After assigning it to the UIBezierPath object, you can release

// your CGPathRef data type safely。

CGPathRelease(cgPath);

如果你選擇混合使用核心圖形函式和UIBezierPath方法,你必須小心地在兩者之間來回移動路徑資訊。因為UIBezierPath物件擁有它的底層CGPathRef資料型別,你不能簡單地檢索該型別並直接修改它。相反,您必須建立一個可變副本,修改副本,然後將副本分配回CGPath屬性,如清單2-4所示。

清單2-4混合了核心圖形和UIBezierPath呼叫

UIBezierPath *aPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 300, 300)];

// Get the CGPathRef and create a mutable version。

CGPathRef cgPath = aPath。CGPath;

CGMutablePathRef mutablePath = CGPathCreateMutableCopy(cgPath);

// Modify the path and assign it back to the UIBezierPath object。

CGPathAddEllipseInRect(mutablePath, NULL, CGRectMake(50, 50, 200, 200));

aPath。CGPath = mutablePath;

// Release both the mutable copy of the path。

CGPathRelease(mutablePath);

呈現Bezier路徑物件的內容

建立UIBezierPath物件之後,您可以在當前圖形上下文中使用它的描邊和填充方法來呈現它。不過,在呼叫這些方法之前,通常需要執行一些其他任務來確保正確繪製路徑:

使用UIColor類的方法設定所需的筆畫和填充顏色。

將形狀放置在目標檢視中需要它的位置。

如果您建立了相對於點(0,0)的路徑,則可以將適當的仿射轉換應用於當前繪圖上下文。例如,要從點(10,10)開始繪製形狀,您可以呼叫CGContextTranslateCTM函式,併為水平和垂直轉換值指定10。調整圖形上下文(與path物件中的點相反)是首選的,因為透過儲存和恢復以前的圖形狀態,可以更容易地撤消更改。

更新path物件的繪圖屬性。當渲染路徑時,你的UIBezierPath例項的繪圖屬性會覆蓋與圖形上下文相關的值。

清單2-5顯示了一個drawRect:方法的示例實現,該方法在自定義檢視中繪製橢圓。橢圓形邊框的左上角位於檢視座標系統中的點(50,50)。因為填充操作一直繪製到路徑邊界,所以該方法在描邊之前填充路徑。這可以防止填充顏色模糊描邊的一半。

在檢視中繪製路徑

- (void)drawRect:(CGRect)rect

{

// Create an oval shape to draw。

UIBezierPath *aPath = [UIBezierPath bezierPathWithOvalInRect:

CGRectMake(0, 0, 200, 100)];

// Set the render colors。

[[UIColor blackColor] setStroke];

[[UIColor redColor] setFill];

CGContextRef aRef = UIGraphicsGetCurrentContext();

// If you have content to draw after the shape,

// save the current state before changing the transform。

//CGContextSaveGState(aRef);

// Adjust the view‘s origin temporarily。 The oval is

// now drawn relative to the new origin point。

CGContextTranslateCTM(aRef, 50, 50);

// Adjust the drawing options as needed。

aPath。lineWidth = 5;

// Fill the path before stroking it so that the fill

// color does not obscure the stroked line。

[aPath fill];

[aPath stroke];

// Restore the graphics state before drawing any other content。

//CGContextRestoreGState(aRef);

}

在一條路徑上進行命中檢測

要確定觸控事件是否發生在路徑的填充部分,您可以使用containsPoint:方法的UIBezierPath。此方法針對path物件中的所有閉合子路徑測試指定點,如果指定點位於這些子路徑上或其中任何一個子路徑中,則返回YES。

重要:containsPoint:方法和核心圖形hit-test函式只在封閉路徑上執行。這些方法總是在開啟的子路徑上返回NO。如果要在開啟的子路徑上執行命中檢測,則必須建立path物件的副本,並在測試點之前關閉開啟的子路徑。

如果您想在路徑的描邊部分(而不是填充區域)進行點選測試,則必須使用核心圖形。CGContextPathContainsPoint函式允許您測試當前分配給圖形上下文的路徑的填充或描邊部分上的點。清單2-6顯示了一個方法,用於測試指定的點是否與指定的路徑相交。填充引數允許呼叫者指定是否應該針對路徑的填充部分或描邊部分測試該點。呼叫者傳入的路徑必須包含一個或多個閉合子路徑,以便成功進行命中檢測。

針對path物件的測試點

- (BOOL)containsPoint:(CGPoint)point onPath:(UIBezierPath *)path inFillArea:(BOOL)inFill

{

CGContextRef context = UIGraphicsGetCurrentContext();

CGPathRef cgPath = path。CGPath;

BOOL isHit = NO;

// Determine the drawing mode to use。 Default to

// detecting hits on the stroked portion of the path。

CGPathDrawingMode mode = kCGPathStroke;

if (inFill)

{

// Look for hits in the fill area of the path instead。

if (path。usesEvenOddFillRule)

mode = kCGPathEOFill;

else

mode = kCGPathFill;

}

// Save the graphics state so that the path can be

// removed later。

CGContextSaveGState(context);

CGContextAddPath(context, cgPath);

// Do the hit detection。

isHit = CGContextPathContainsPoint(context, point, mode);

CGContextRestoreGState(context);

return isHit;

}

CAShapeLayer繼承自CALayer,可使用CALayer的所有屬性

CAShapeLayer需要和貝塞爾曲線配合使用才有意義。貝塞爾曲線可以為其提供形狀,而單獨使用CAShapeLayer是沒有任何意義的。

使用CAShapeLayer與貝塞爾曲線可以實現不在view的DrawRect方法中畫出一些想要的圖形

關於CAShapeLayer和DrawRect的比較

DrawRect:DrawRect屬於CoreGraphic框架,佔用CPU,消耗效能大

CAShapeLayer:CAShapeLayer屬於CoreAnimation框架,透過GPU來渲染圖形,節省效能。動畫渲染直接提交給手機GPU,不消耗記憶體

如:

CGSize finalSize = CGSizeMake(CGRectGetWidth(self。view。frame), 600);

CGFloat layerHeight = finalSize。height * 0。2;

CAShapeLayer *bottomCurveLayer = [[CAShapeLayer alloc]init];

UIBezierPath *path = [[UIBezierPath alloc]init];

[path moveToPoint:CGPointMake(0, finalSize。height - layerHeight)];

[path addLineToPoint:CGPointMake(0, finalSize。height - 1)];

[path addLineToPoint:CGPointMake(finalSize。width, finalSize。height - 1)];

[path addLineToPoint:CGPointMake(finalSize。width, finalSize。height - layerHeight)];

[path addQuadCurveToPoint:CGPointMake(0, finalSize。height - layerHeight) controlPoint:CGPointMake(finalSize。width / 2, (finalSize。height - layerHeight) - 40)];

bottomCurveLayer。path = path。CGPath;

bottomCurveLayer。fillColor = [UIColor orangeColor]。CGColor;

[self。view。layer addSublayer:bottomCurveLayer];

繪製和建立影象

大多數情況下,使用標準檢視顯示影象是相當簡單的。然而,有兩種情況你可能需要做額外的工作:

如果您想要將影象顯示為自定義檢視的一部分,您必須自己在檢視的drawRect:方法中繪製圖像。畫圖解釋了這一點。

如果你想在螢幕外渲染影象(稍後繪製,或者儲存到檔案中),你必須建立一個位圖影象上下文。要了解更多資訊,請閱讀使用點陣圖圖形上下文建立新影象。

畫圖片

為了獲得最大的效能,如果你的影象繪製需要可以使用UIImageView類來滿足,你應該使用這個影象物件來初始化一個UIImageView物件。但是,如果您需要顯式地繪製圖像,您可以儲存影象並稍後在檢視的drawRect:方法中使用它。

下面的示例演示如何從應用程式的包載入影象。

NSString *imagePath = [[NSBundle mainBundle] pathForResource:@“myImage” ofType:@“png”];

UIImage *myImageObj = [[UIImage alloc] initWithContentsOfFile:imagePath];

// Store the image into a property of type UIImage *

// for use later in the class’s drawRect: method。

self。anImage = myImageObj;

要在檢視的drawRect:方法中顯式地繪製結果影象,你可以使用UIImage中可用的任何繪圖方法。這些方法允許您指定要在檢視中繪製圖像的位置,因此不需要在繪製之前建立和應用單獨的轉換。

下面的程式碼片段繪製了在檢視中(10,10)處載入的影象。

- (void)drawRect:(CGRect)rect

{

。。。

// Draw the image。

[self。anImage drawAtPoint:CGPointMake(10, 10)];

}

使用點陣圖圖形上下文建立新影象

大多數時候,當你畫畫的時候,你的目標是在螢幕上顯示一些東西。然而,有時將某些內容繪製到螢幕外緩衝區是有用的。例如,您可能希望建立一個現有影象的縮圖,將其繪製到緩衝區中,以便將其儲存到檔案中,等等。為了支援這些需求,您可以建立一個位圖影象上下文,使用UIKit框架或核心圖形函式來繪製它,然後從上下文獲取一個影象物件。

在UIKit中,程式如下:

呼叫UIGraphicsBeginImageContextWithOptions來建立點陣圖上下文並將其推入圖形堆疊。

對於第一個引數(size),傳遞一個CGSize值來指定點陣圖上下文的維數(以點為單位)。

對於第二個引數(不透明),如果您的影象包含透明(alpha通道),則傳遞NO。否則,透過YES來最大化效能。

對於最後的引數(縮放),為裝置的主螢幕適當縮放的點陣圖傳遞0。0,或者傳遞您選擇的縮放因子。

例如,下面的程式碼片段建立了一個200 x 200畫素的點陣圖。(畫素的數目是由影象的大小乘以比例係數來決定的。)

UIGraphicsBeginImageContextWithOptions(CGSizeMake(100。0,100。0), NO, 2。0);

你通常應該避免呼叫類似的UIGraphicsBeginImageContext函式(除了作為向後相容的回退),因為它總是建立縮放係數為1。0的影象。如果底層裝置有一個高解析度的螢幕,用UIGraphicsBeginImageContext建立的影象在渲染時可能不會顯示得那麼平滑。

使用UIKit或核心圖形例程將影象的內容繪製到新建立的圖形上下文中。

呼叫UIGraphicsGetImageFromCurrentImageContext函式來生成並返回一個基於你所繪製的UIImage物件。如果需要,您可以繼續繪圖並再次呼叫此方法來生成其他影象。

呼叫UIGraphicsEndImageContext從圖形堆疊彈出上下文。

清單3-1中的方法透過Internet下載影象並將其繪製到基於影象的上下文中,縮小到應用程式圖示的大小。然後,它獲得一個由點陣圖資料建立的UIImage物件,並將其分配給一個例項變數。注意點陣圖的大小(UIGraphicsBeginImageContextWithOptions的第一個引數)和繪製內容的大小(imageRect的大小)應該匹配。如果內容比點陣圖大,部分內容將被剪下,不會出現在結果影象中。

清單3-1將縮小後的影象繪製到點陣圖上下文中,並獲得結果影象

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {

UIImage *image = [[UIImage alloc] initWithData:self。activeDownload];

if (image != nil && image。size。width != kAppIconHeight && image。size。height != kAppIconHeight) {

CGRect imageRect = CGRectMake(0。0, 0。0, kAppIconHeight, kAppIconHeight);

UIGraphicsBeginImageContextWithOptions(itemSize, NO, [UIScreen mainScreen]。scale);

[image drawInRect:imageRect];

self。appRecord。appIcon = UIGraphicsGetImageFromCurrentImageContext(); // UIImage returned。

UIGraphicsEndImageContext();

} else {

self。appRecord。appIcon = image;

}

self。activeDownload = nil;

[image release];

self。imageConnection = nil;

[delegate appImageDidLoad:self。indexPathInTableView];

}

還可以呼叫核心圖形函式來繪製生成的點陣圖影象的內容;清單3-2中的程式碼片段給出了一個示例,它繪製了一個縮小的PDF頁面影象。注意,程式碼在呼叫CGContextDrawPDFPage之前翻轉圖形上下文,使繪製的影象與UIKit的預設座標系統對齊。

清單3-2使用核心圖形函式繪製點陣圖上下文

// Other code precedes。。。

CGRect pageRect = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);

pdfScale = self。frame。size。width/pageRect。size。width;

pageRect。size = CGSizeMake(pageRect。size。width * pdfScale, pageRect。size。height * pdfScale);

UIGraphicsBeginImageContextWithOptions(pageRect。size, YES, pdfScale);

CGContextRef context = UIGraphicsGetCurrentContext();

// First fill the background with white。

CGContextSetRGBFillColor(context, 1。0,1。0,1。0,1。0);

CGContextFillRect(context,pageRect);

CGContextSaveGState(context);

// Flip the context so that the PDF page is rendered right side up

CGContextTranslateCTM(context, 0。0, pageRect。size。height);

CGContextScaleCTM(context, 1。0, -1。0);

// Scale the context so that the PDF page is rendered at the

// correct size for the zoom level。

CGContextScaleCTM(context, pdfScale,pdfScale);

CGContextDrawPDFPage(context, page);

CGContextRestoreGState(context);

UIImage *backgroundImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

backgroundImageView = [[UIImageView alloc] initWithImage:backgroundImage];

// Other code follows。。。

如果您喜歡完全使用核心圖形來繪製點陣圖圖形上下文,您可以使用CGBitmapContextCreate函式來建立上下文並將影象內容繪製到其中。完成繪圖後,呼叫CGBitmapContextCreateImage函式從點陣圖上下文獲取CGImageRef物件。你可以直接繪製核心圖形影象或者使用它來初始化一個UIImage物件。完成後,在圖形上下文中呼叫CGContextRelease函式。

建立一個新的PDF檔案

// Prepare the text using a Core Text Framesetter。

CFAttributedStringRef currentText = CFAttributedStringCreate(NULL, (CFStringRef)textView。text, NULL);

if (currentText) {

CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(currentText);

if (framesetter) {

NSString *pdfFileName = [self getPDFFileName];

// Create the PDF context using the default page size of 612 x 792。

UIGraphicsBeginPDFContextToFile(pdfFileName, CGRectZero, nil);

CFRange currentRange = CFRangeMake(0, 0);

NSInteger currentPage = 0;

BOOL done = NO;

do {

// Mark the beginning of a new page。

UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, 612, 792), nil);

// Draw a page number at the bottom of each page。

currentPage++;

[self drawPageNumber:currentPage];

// Render the current page and update the current range to

// point to the beginning of the next page。

currentRange = [self renderPageWithTextRange:currentRange andFramesetter:framesetter];

// If we‘re at the end of the text, exit the loop。

if (currentRange。location == CFAttributedStringGetLength((CFAttributedStringRef)currentText))

done = YES;

} while (!done);

// Close the PDF context and write the contents out。

UIGraphicsEndPDFContext();

// Release the framewetter。

CFRelease(framesetter);

} else {

NSLog(@“Could not create the framesetter needed to lay out the atrributed string。”);

}

// Release the attributed string。

CFRelease(currentText);

} else {

NSLog(@“Could not create the attributed string for the framesetter”);

}

}

}

畫PDF頁

所有PDF繪圖必須在頁面的上下文中完成。每個PDF文件至少有一個頁面,許多可能有多個頁面。你可以透過呼叫UIGraphicsBeginPDFPage或UIGraphicsBeginPDFPageWithInfo函式來指定一個新頁面的開始。這些函式關閉前面的頁面(如果打開了一個頁面),建立一個新頁面,併為繪圖做好準備。UIGraphicsBeginPDFPage使用預設大小建立新頁面,而UIGraphicsBeginPDFPageWithInfo函式允許您自定義頁面大小或自定義PDF頁面的其他方面。

建立頁面之後,所有後續的繪圖命令都將被PDF圖形上下文捕獲並轉換為PDF命令。你可以在頁面中繪製任何你想要的東西,包括文字、向量形狀和影象,就像你在應用程式的自定義檢視中所做的一樣。您發出的繪圖命令由PDF上下文捕獲並轉換為PDF資料。內容在頁面上的放置完全由您決定,但是必須在頁面的邊框內進行。

清單4-2顯示了用於在PDF頁面中繪製內容的兩個自定義方法。renderPageWithTextRange:and framesetter:方法使用核心文字建立一個適合頁面的文字框架,然後在框架內佈局一些文字。在佈局文字之後,它返回一個更新後的範圍,該範圍反映當前頁面的結束和下一頁的開始。方法使用NSString繪圖功能在每個PDF頁面的底部繪製頁碼字串。

清單4-2繪製基於頁面的內容

// Use Core Text to draw the text in a frame on the page。

- (CFRange)renderPage:(NSInteger)pageNum withTextRange:(CFRange)currentRange

andFramesetter:(CTFramesetterRef)framesetter

{

// Get the graphics context。

CGContextRef currentContext = UIGraphicsGetCurrentContext();

// Put the text matrix into a known state。 This ensures

// that no old scaling factors are left in place。

CGContextSetTextMatrix(currentContext, CGAffineTransformIdentity);

// Create a path object to enclose the text。 Use 72 point

// margins all around the text。

CGRect frameRect = CGRectMake(72, 72, 468, 648);

CGMutablePathRef framePath = CGPathCreateMutable();

CGPathAddRect(framePath, NULL, frameRect);

// Get the frame that will do the rendering。

// The currentRange variable specifies only the starting point。 The framesetter

// lays out as much text as will fit into the frame。

CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, NULL);

CGPathRelease(framePath);

// Core Text draws from the bottom-left corner up, so flip

// the current transform prior to drawing。

CGContextTranslateCTM(currentContext, 0, 792);

CGContextScaleCTM(currentContext, 1。0, -1。0);

// Draw the frame。

CTFrameDraw(frameRef, currentContext);

// Update the current range based on what was drawn。

currentRange = CTFrameGetVisibleStringRange(frameRef);

currentRange。location += currentRange。length;

currentRange。length = 0;

CFRelease(frameRef);

return currentRange;

}

- (void)drawPageNumber:(NSInteger)pageNum

{

NSString *pageString = [NSString stringWithFormat:@“Page %d”, pageNum];

UIFont *theFont = [UIFont systemFontOfSize:12];

CGSize maxSize = CGSizeMake(612, 72);

CGSize pageStringSize = [pageString sizeWithFont:theFont

constrainedToSize:maxSize

lineBreakMode:UILineBreakModeClip];

CGRect stringRect = CGRectMake(((612。0 - pageStringSize。width) / 2。0),

720。0 + ((72。0 - pageStringSize。height) / 2。0),

pageStringSize。width,

pageStringSize。height);

[pageString drawInRect:stringRect withFont:theFont];

}