Xamarin.iOS 中的核心圖形
本文討論核心圖形 iOS 架構。 它示範如何使用核心圖形來繪製幾何、影像和 PDF。
iOS 包含 核心圖形 架構,以提供低階繪圖支援。 這些架構是可在UIKit中啟用豐富圖形化功能的功能。
核心圖形是低階 2D 圖形架構,可讓繪圖裝置獨立圖形。 UIKit 中的所有 2D 繪圖都會在內部使用核心圖形。
核心圖形支援在數個案例中繪製,包括:
幾何空間
不論案例為何,使用核心圖形完成的所有繪圖都是在幾何空間中完成,這表示它會在抽象點而非像素中運作。 您會以幾何和繪圖狀態來描述您想要繪製的內容,例如色彩、線條樣式等,而核心圖形處理會將所有專案轉譯成圖元。 這類狀態會新增至圖形內容,您可以將其想像成畫家的畫布。
此方法有幾個優點:
- 繪圖程式代碼會變成動態,而且後續可以在運行時間修改圖形。
- 減少應用程式套件組合中靜態映像的需求,可以減少應用程式大小。
- 圖形在裝置上的解析度變更會變得更有彈性。
在UIView子類別中繪製
每個 都有 UIView
一個 Draw
方法,在需要繪製時由系統呼叫。 若要將繪圖程式代碼新增至檢視,子類別 UIView
和覆寫 Draw
:
public class TriangleView : UIView
{
public override void Draw (CGRect rect)
{
base.Draw (rect);
}
}
絕對不應該直接呼叫繪製。 系統會在執行循環處理期間呼叫它。 第一次在檢視加入至檢視階層之後執行迴圈,就會呼叫其 Draw
方法。 當檢視標示為需要透過呼叫 或 SetNeedsDisplayInRect
檢視來繪製檢視時,會發生後續呼叫。Draw
SetNeedsDisplay
圖形程式代碼的模式
實作中的 Draw
程式代碼應該描述其想要繪製的內容。 繪圖程式代碼遵循一個模式,它會設定一些繪圖狀態,並呼叫 方法來要求繪製它。 此模式可以一般化,如下所示:
取得圖形內容。
設定繪圖屬性。
從繪圖基本類型建立一些幾何。
呼叫 Draw 或 Stroke 方法。
基本繪圖範例
例如,請考慮下列程式碼片段:
//get graphics context
using (CGContext g = UIGraphics.GetCurrentContext ()) {
//set up drawing attributes
g.SetLineWidth (10);
UIColor.Blue.SetFill ();
UIColor.Red.SetStroke ();
//create geometry
var path = new CGPath ();
path.AddLines (new CGPoint[]{
new CGPoint (100, 200),
new CGPoint (160, 100),
new CGPoint (220, 200)});
path.CloseSubpath ();
//add geometry to graphics context and draw it
g.AddPath (path);
g.DrawPath (CGPathDrawingMode.FillStroke);
}
讓我們將此程式代碼細分為:
using (CGContext g = UIGraphics.GetCurrentContext ()) {
...
}
使用這一行,它會先取得用於繪圖的目前圖形內容。 您可以將圖形內容視為繪圖發生所在畫布,其中包含繪圖的所有狀態,例如筆劃和填滿色彩,以及要繪製的幾何。
g.SetLineWidth (10);
UIColor.Blue.SetFill ();
UIColor.Red.SetStroke ();
取得圖形內容之後,程式代碼會設定繪圖時要使用的一些屬性,如上所示。 在此情況下,會設定線條寬度、筆劃和填滿色彩。 接著,任何後續的繪圖都會使用這些屬性,因為它們會維持在圖形內容的狀態中。
若要建立幾何,程式代碼會使用 CGPath
,其允許從線條和曲線描述圖形路徑。 在此情況下,路徑會新增連接點數位的線條,以構成三角形。 如核心圖形下方所示,使用座標系統來檢視繪圖,其中原點位於左上方,右邊為正 x,而正-y 方向往下:
var path = new CGPath ();
path.AddLines (new CGPoint[]{
new CGPoint (100, 200),
new CGPoint (160, 100),
new CGPoint (220, 200)});
path.CloseSubpath ();
建立路徑之後,它會新增至圖形內容,以便呼叫 AddPath
和 DrawPath
分別繪製它。
產生的檢視如下所示:
建立漸層填滿
也提供更豐富的繪圖形式。 例如,核心圖形允許建立漸層填滿並套用裁剪路徑。 若要在上一個範例的路徑內繪製漸層填滿,請先將路徑設定為裁剪路徑:
// add the path back to the graphics context so that it is the current path
g.AddPath (path);
// set the current path to be the clipping path
g.Clip ();
將目前路徑設定為裁剪路徑會限制路徑幾何內的所有後續繪圖,例如下列程序代碼,這會繪製線性漸層:
// the color space determines how Core Graphics interprets color information
using (CGColorSpace rgb = CGColorSpace.CreateDeviceRGB()) {
CGGradient gradient = new CGGradient (rgb, new CGColor[] {
UIColor.Blue.CGColor,
UIColor.Yellow.CGColor
});
// draw a linear gradient
g.DrawLinearGradient (
gradient,
new CGPoint (path.BoundingBox.Left, path.BoundingBox.Top),
new CGPoint (path.BoundingBox.Right, path.BoundingBox.Bottom),
CGGradientDrawingOptions.DrawsBeforeStartLocation);
}
這些變更會產生漸層填滿,如下所示:
修改線條模式
線條的繪圖屬性也可以使用核心圖形來修改。 這包括變更線條寬度和筆劃色彩,以及線條圖樣本身,如下列程式代碼所示:
//use a dashed line
g.SetLineDash (0, new nfloat[] { 10, 4 * (nfloat)Math.PI });
在任何繪圖作業之前新增此程式代碼會導致虛線筆劃長 10 單位,虛線之間的間距為 4 單位,如下所示:
請注意,在 Xamarin.iOS 中使用整合 API 時,陣列類型必須是 nfloat
,而且也必須明確地轉換成 Math.PI。
繪製影像和文字
除了在檢視形內容中繪製路徑之外,核心圖形也支援繪製影像和文字。 若要繪製影像,只需建立 CGImage
,並將其傳遞至 DrawImage
呼叫:
public override void Draw (CGRect rect)
{
base.Draw (rect);
using(CGContext g = UIGraphics.GetCurrentContext ()){
g.DrawImage (rect, UIImage.FromFile ("MyImage.png").CGImage);
}
}
不過,這會產生倒置繪製的影像,如下所示:
這是因為影像繪製的核心圖形原點位於左下方,而檢視的原點則位於左上方。 因此,若要正確顯示影像,必須修改原點,這可以藉由修改 目前的轉換矩陣 (CTM) 來完成。 CTM 會定義點所在的位置,也稱為 用戶空間。 反轉 y 方向的 CTM,並以負 Y 方向的周框高度移動 CTM 可以翻轉影像。
圖形內容具有可轉換 CTM 的協助程式方法。 在此情況下, ScaleCTM
會「翻轉」繪圖並將 TranslateCTM
它移到左上方,如下所示:
public override void Draw (CGRect rect)
{
base.Draw (rect);
using (CGContext g = UIGraphics.GetCurrentContext ()) {
// scale and translate the CTM so the image appears upright
g.ScaleCTM (1, -1);
g.TranslateCTM (0, -Bounds.Height);
g.DrawImage (rect, UIImage.FromFile ("MyImage.png").CGImage);
}
產生的影像接著會直立顯示:
重要
圖形內容的變更會套用至所有後續的繪圖作業。 因此,當CTM轉換時,它會影響任何其他繪圖。 例如,如果您在CTM轉換之後繪製三角形,它就會顯示為倒置。
將文字新增至影像
如同路徑和影像,使用核心圖形繪製文字牽涉到設定某些圖形狀態和呼叫繪製方法的相同基本模式。 在文字的情況下,顯示文字的方法為 ShowText
。 新增至影像繪圖範例時,下列程式代碼會使用核心圖形繪製一些文字:
public override void Draw (RectangleF rect)
{
base.Draw (rect);
// image drawing code omitted for brevity ...
// translate the CTM by the font size so it displays on screen
float fontSize = 35f;
g.TranslateCTM (0, fontSize);
// set general-purpose graphics state
g.SetLineWidth (1.0f);
g.SetStrokeColor (UIColor.Yellow.CGColor);
g.SetFillColor (UIColor.Red.CGColor);
g.SetShadow (new CGSize (5, 5), 0, UIColor.Blue.CGColor);
// set text specific graphics state
g.SetTextDrawingMode (CGTextDrawingMode.FillStroke);
g.SelectFont ("Helvetica", fontSize, CGTextEncoding.MacRoman);
// show the text
g.ShowText ("Hello Core Graphics");
}
如您所見,設定文字繪圖的圖形狀態類似於繪圖幾何。 不過,對於文字繪圖,文字繪圖模式和字型也會套用。 在此情況下,也會套用陰影,不過套用陰影在路徑繪圖上的運作方式相同。
產生的文字會與影像一起顯示,如下所示:
記憶體支援的映像
除了繪圖到檢視的圖形內容之外,Core Graphics 還支援繪製記憶體支援的影像,也稱為從螢幕外繪製。 這樣做需要:
- 建立記憶體位圖中支援的圖形內容
- 設定繪圖狀態和發出繪圖命令
- 從內容取得影像
- 拿掉內容
不同於檢視 Draw
提供內容的方法,在此情況下,您會以下列兩種方式之一建立內容:
通話
UIGraphics.BeginImageContext
(或BeginImageContextWithOptions
)藉由建立新的
CGBitmapContextInstance
CGBitmapContextInstance
當您直接使用影像位時會很有用,例如當您使用自定義影像操作演算法的情況。 在其他所有情況下,您應該使用 BeginImageContext
或 BeginImageContextWithOptions
。
擁有影像內容之後,新增繪圖程序代碼就如同在子類別中一 UIView
樣。 例如,先前用來繪製三角形的程式碼範例可用來繪製記憶體中的影像,而不是 在 中 UIView
繪製,如下所示:
UIImage DrawTriangle ()
{
UIImage triangleImage;
//push a memory backed bitmap context on the context stack
UIGraphics.BeginImageContext (new CGSize (200.0f, 200.0f));
//get graphics context
using(CGContext g = UIGraphics.GetCurrentContext ()){
//set up drawing attributes
g.SetLineWidth(4);
UIColor.Purple.SetFill ();
UIColor.Black.SetStroke ();
//create geometry
path = new CGPath ();
path.AddLines(new CGPoint[]{
new CGPoint(100,200),
new CGPoint(160,100),
new CGPoint(220,200)});
path.CloseSubpath();
//add geometry to graphics context and draw it
g.AddPath(path);
g.DrawPath(CGPathDrawingMode.FillStroke);
//get a UIImage from the context
triangleImage = UIGraphics.GetImageFromCurrentImageContext ();
}
return triangleImage;
}
從任何 UIView
擷取影像,常見的繪製到記憶體支援的位圖是擷取影像。 例如,下列程式代碼會將檢視的圖層轉譯為位圖內容,並從中建立 UIImage
:
UIGraphics.BeginImageContext (cellView.Frame.Size);
//render the view's layer in the current context
anyView.Layer.RenderInContext (UIGraphics.GetCurrentContext ());
//get a UIImage from the context
UIImage anyViewImage = UIGraphics.GetImageFromCurrentImageContext ();
UIGraphics.EndImageContext ();
繪圖 PDF
除了影像之外,核心圖形還支援 PDF 繪圖。 如同影像,您可以在記憶體中轉譯 PDF,以及讀取 PDF 以在 中 UIView
轉譯。
UIView 中的 PDF
核心圖形也支援從檔案讀取 PDF,並使用 類別在檢視 CGPDFDocument
中轉譯。 類別 CGPDFDocument
代表程式代碼中的 PDF,可用來讀取和繪製頁面。
例如,子類別中的 UIView
下列程式代碼會將檔案中的 PDF 讀取到 CGPDFDocument
:
public class PDFView : UIView
{
CGPDFDocument pdfDoc;
public PDFView ()
{
//create a CGPDFDocument from file.pdf included in the main bundle
pdfDoc = CGPDFDocument.FromFile ("file.pdf");
}
public override void Draw (Rectangle rect)
{
...
}
}
Draw
方法接著CGPDFDocument
可以使用 來讀取頁面CGPDFPage
,並藉由呼叫 DrawPDFPage
來轉譯頁面,如下所示:
public override void Draw (CGRect rect)
{
base.Draw (rect);
//flip the CTM so the PDF will be drawn upright
using (CGContext g = UIGraphics.GetCurrentContext ()) {
g.TranslateCTM (0, Bounds.Height);
g.ScaleCTM (1, -1);
// render the first page of the PDF
using (CGPDFPage pdfPage = pdfDoc.GetPage (1)) {
//get the affine transform that defines where the PDF is drawn
CGAffineTransform t = pdfPage.GetDrawingTransform (CGPDFBox.Crop, rect, 0, true);
//concatenate the pdf transform with the CTM for display in the view
g.ConcatCTM (t);
//draw the pdf page
g.DrawPDFPage (pdfPage);
}
}
}
記憶體支援的 PDF
針對記憶體內部 PDF,您必須呼叫 BeginPDFContext
來建立 PDF 內容。 繪圖到 PDF 對頁面是細微的。 每個頁面都是藉由呼叫 BeginPDFPage
和 完成,並在兩者之間呼叫 EndPDFContent
圖形程式代碼來完成。 此外,與影像繪圖一樣,記憶體支援的 PDF 繪圖會使用左下方的原點,其可藉由修改 CTM,就像使用影像一樣來考慮。
下列程式代碼示範如何將文字繪製到 PDF:
//data buffer to hold the PDF
NSMutableData data = new NSMutableData ();
//create a PDF with empty rectangle, which will configure it for 8.5x11 inches
UIGraphics.BeginPDFContext (data, CGRect.Empty, null);
//start a PDF page
UIGraphics.BeginPDFPage ();
using (CGContext g = UIGraphics.GetCurrentContext ()) {
g.ScaleCTM (1, -1);
g.TranslateCTM (0, -25);
g.SelectFont ("Helvetica", 25, CGTextEncoding.MacRoman);
g.ShowText ("Hello Core Graphics");
}
//complete a PDF page
UIGraphics.EndPDFContent ();
產生的文字會繪製到 PDF,然後包含在可以儲存、上傳、電子郵件等的 中 NSData
。
摘要
在本文中,我們已探討透過 核心圖形架構提供的圖形 功能。 我們已瞭解如何使用核心圖形在 的內容 UIView,
中繪製幾何、影像和 PDF,以及記憶體支援的圖形內容。