Grafica principale in Xamarin.iOS
Questo articolo illustra i framework iOS core della grafica. Illustra come usare Core Graphics per disegnare geometria, immagini e PDF.
iOS include il framework Core Graphics per fornire supporto di disegno di basso livello. Questi framework sono ciò che abilita le funzionalità grafiche avanzate all'interno di UIKit.
Core Graphics è un framework grafico 2D di basso livello che consente di disegnare grafica indipendente dal dispositivo. Tutto il disegno 2D in UIKit usa internamente la grafica principale.
Core Graphics supporta il disegno in diversi scenari, tra cui:
- Disegno sullo schermo tramite un oggetto
UIView
. - Disegno di immagini in memoria o sullo schermo.
- Creazione e disegno in un PDF.
- Lettura e disegno di un PDF esistente.
Spazio geometrico
Indipendentemente dallo scenario, tutto il disegno eseguito con Core Graphics viene eseguito nello spazio geometrico, ovvero funziona in punti astratti anziché in pixel. Descrivere ciò che si desidera disegnare in termini di geometria e stato di disegno, ad esempio colori, stili di linea e così via, e core graphics gestisce la conversione di tutto in pixel. Tale stato viene aggiunto a un contesto grafico, che si può pensare come la tela di un pittore.
Questo approccio offre alcuni vantaggi:
- Il codice di disegno diventa dinamico e può successivamente modificare grafica in fase di esecuzione.
- La riduzione della necessità di immagini statiche nel bundle dell'applicazione può ridurre le dimensioni dell'applicazione.
- La grafica diventa più resiliente alle modifiche di risoluzione nei dispositivi.
Disegno in una sottoclasse UIView
Ogni UIView
oggetto ha un Draw
metodo chiamato dal sistema quando deve essere disegnato. Per aggiungere codice di disegno a una vista, sottoclasse UIView
ed eseguire l'override Draw
di :
public class TriangleView : UIView
{
public override void Draw (CGRect rect)
{
base.Draw (rect);
}
}
Draw non deve mai essere chiamato direttamente. Viene chiamato dal sistema durante l'elaborazione del ciclo di esecuzione. La prima volta che si esegue il ciclo di esecuzione dopo l'aggiunta di una vista alla gerarchia di visualizzazione, viene chiamato il relativo Draw
metodo. Le chiamate successive da Draw
eseguire quando la vista è contrassegnata come necessario per essere disegnata chiamando SetNeedsDisplay
o SetNeedsDisplayInRect
nella vista.
Modello per il codice grafico
Il codice nell'implementazione Draw
deve descrivere ciò che vuole disegnare. Il codice di disegno segue un modello in cui imposta uno stato di disegno e chiama un metodo per richiederlo. Questo modello può essere generalizzato come segue:
Ottenere un contesto grafico.
Configurare gli attributi di disegno.
Creare una geometria dalle primitive di disegno.
Chiamare un metodo Draw o Stroke.
Esempio di disegno di base
Si consideri il frammento di codice di esempio seguente:
//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);
}
Di seguito è riportato un'interruzione del codice:
using (CGContext g = UIGraphics.GetCurrentContext ()) {
...
}
Con questa linea, ottiene innanzitutto il contesto grafico corrente da utilizzare per il disegno. È possibile considerare un contesto grafico come l'area di disegno su cui si verifica il disegno, contenente tutto lo stato del disegno, ad esempio i colori di tratto e riempimento, nonché la geometria da disegnare.
g.SetLineWidth (10);
UIColor.Blue.SetFill ();
UIColor.Red.SetStroke ();
Dopo aver ottenuto un contesto grafico, il codice configura alcuni attributi da usare durante il disegno, come illustrato sopra. In questo caso vengono impostati la larghezza della linea, i colori di tratto e di riempimento. Tutti i disegni successivi useranno quindi questi attributi perché vengono mantenuti nello stato del contesto grafico.
Per creare una geometria, il codice usa un CGPath
oggetto , che consente di descrivere un percorso grafico da linee e curve. In questo caso, il percorso aggiunge linee che collegano una matrice di punti per creare un triangolo. Come illustrato sotto Core Graphics usa un sistema di coordinate per il disegno di visualizzazione, in cui l'origine si trova in alto a sinistra, con x-direct positivo verso destra e direzione positiva-y verso il basso:
var path = new CGPath ();
path.AddLines (new CGPoint[]{
new CGPoint (100, 200),
new CGPoint (160, 100),
new CGPoint (220, 200)});
path.CloseSubpath ();
Dopo aver creato il percorso, viene aggiunto al contesto grafico in modo che la chiamata AddPath
e DrawPath
rispettivamente possa disegnare il percorso.
La visualizzazione risultante è illustrata di seguito:
Creazione di riempimenti sfumato
Sono disponibili anche forme più avanzate di disegno. Ad esempio, Core Graphics consente di creare riempimenti sfumato e di applicare percorsi di ritaglio. Per disegnare un riempimento sfumato all'interno del percorso dell'esempio precedente, prima di tutto il percorso deve essere impostato come percorso di ritaglio:
// 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 ();
L'impostazione del percorso corrente come percorso di ritaglio vincola tutto il disegno successivo all'interno della geometria del percorso, ad esempio il codice seguente, che disegna una sfumatura lineare:
// 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);
}
Queste modifiche producono un riempimento sfumato come illustrato di seguito:
Modifica dei modelli di linea
Gli attributi di disegno delle linee possono anche essere modificati con Core Graphics. Ciò include la modifica della larghezza della linea e del colore del tratto, nonché il motivo di linea stesso, come illustrato nel codice seguente:
//use a dashed line
g.SetLineDash (0, new nfloat[] { 10, 4 * (nfloat)Math.PI });
L'aggiunta di questo codice prima di qualsiasi operazione di disegno comporta tratti tratteggiati lunghi 10 unità, con 4 unità di spaziatura tra trattini, come illustrato di seguito:
Si noti che quando si usa l'API unificata in Xamarin.iOS, il tipo di matrice deve essere un nfloat
e deve anche essere eseguito in modo esplicito a Math.PI.
Disegno di immagini e testo
Oltre ai percorsi di disegno nel contesto grafico di una visualizzazione, Core Graphics supporta anche il disegno di immagini e testo. Per disegnare un'immagine, è sufficiente crearne una CGImage
e passarla a una DrawImage
chiamata:
public override void Draw (CGRect rect)
{
base.Draw (rect);
using(CGContext g = UIGraphics.GetCurrentContext ()){
g.DrawImage (rect, UIImage.FromFile ("MyImage.png").CGImage);
}
}
Tuttavia, questo produce un'immagine disegnata a capovolto, come illustrato di seguito:
Il motivo è l'origine grafica principale per il disegno dell'immagine in basso a sinistra, mentre la visualizzazione ha la sua origine in alto a sinistra. Pertanto, per visualizzare correttamente l'immagine, l'origine deve essere modificata, che può essere eseguita modificando la matrice di trasformazione corrente (CTM). Il CTM definisce dove si trovano i punti, noti anche come spazio utente. Invertire il CTM nella direzione y e spostarlo in base all'altezza dei limiti nella direzione negativa y può capovolgere l'immagine.
Il contesto grafico include metodi helper per trasformare il CTM. In questo caso, ScaleCTM
"capovolge" il disegno e TranslateCTM
lo sposta in alto a sinistra, come illustrato di seguito:
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);
}
L'immagine risultante viene quindi visualizzata in verticale:
Importante
Le modifiche apportate al contesto grafico si applicano a tutte le operazioni di disegno successive. Pertanto, quando il CTM viene trasformato, influisce su qualsiasi disegno aggiuntivo. Ad esempio, se il triangolo è stato disegnato dopo la trasformazione CTM, apparirebbe capovolto.
Aggiunta di testo all'immagine
Come per i percorsi e le immagini, il disegno di testo con Core Graphics prevede lo stesso modello di base per impostare uno stato grafico e chiamare un metodo da disegnare. Nel caso di testo, il metodo per visualizzare il testo è ShowText
. Quando viene aggiunto all'esempio di disegno dell'immagine, il codice seguente disegna testo usando Core Graphics:
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");
}
Come si può notare, l'impostazione dello stato grafico per il disegno di testo è simile alla geometria di disegno. Per il disegno di testo, tuttavia, vengono applicate anche la modalità di disegno del testo e il tipo di carattere. In questo caso, viene applicata anche un'ombreggiatura, anche se l'applicazione di ombreggiature funziona allo stesso modo per il disegno del tracciato.
Il testo risultante viene visualizzato con l'immagine come illustrato di seguito:
Immagini supportate dalla memoria
Oltre a disegnare nel contesto grafico di una visualizzazione, Core Graphics supporta le immagini supportate dalla memoria di disegno, note anche come disegno fuori schermo. In questo modo è necessario:
- Creazione di un contesto grafico supportato da una bitmap in memoria
- Impostazione dello stato del disegno ed esecuzione di comandi di disegno
- Recupero dell'immagine dal contesto
- Rimozione del contesto
A differenza del Draw
metodo , in cui il contesto viene fornito dalla vista, in questo caso si crea il contesto in uno dei due modi seguenti:
UIGraphics.BeginImageContext
Chiamando (oBeginImageContextWithOptions
)Creando un nuovo
CGBitmapContextInstance
CGBitmapContextInstance
è utile quando si lavora direttamente con i bit dell'immagine, ad esempio nei casi in cui si usa un algoritmo di manipolazione delle immagini personalizzato. In tutti gli altri casi, è consigliabile usare BeginImageContext
o BeginImageContextWithOptions
.
Dopo aver creato un contesto di immagine, l'aggiunta di codice di disegno è simile a quella in una UIView
sottoclasse. Ad esempio, l'esempio di codice usato in precedenza per disegnare un triangolo può essere usato per disegnare in un'immagine in memoria anziché in un UIView
, come illustrato di seguito:
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;
}
Un uso comune del disegno in una bitmap supportata dalla memoria consiste nell'acquisire un'immagine da qualsiasi UIView
oggetto . Ad esempio, il codice seguente esegue il rendering del livello di una visualizzazione in un contesto bitmap e ne crea uno 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 ();
Disegno di PDF
Oltre alle immagini, Core Graphics supporta il disegno PDF. Come le immagini, è possibile eseguire il rendering di un PDF in memoria, nonché leggere un PDF per il rendering in un oggetto UIView
.
PDF in un controllo UIView
Core Graphics supporta anche la lettura di un PDF da un file e il rendering in una visualizzazione usando la CGPDFDocument
classe . La CGPDFDocument
classe rappresenta un PDF nel codice e può essere utilizzata per leggere e disegnare pagine.
Ad esempio, il codice seguente in una UIView
sottoclasse legge un PDF da un file in un CGPDFDocument
oggetto :
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)
{
...
}
}
Il Draw
metodo può quindi usare per CGPDFDocument
leggere una pagina ed CGPDFPage
eseguirne il rendering chiamando DrawPDFPage
, come illustrato di seguito:
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 con supporto per la memoria
Per un PDF in memoria, è necessario creare un contesto PDF chiamando BeginPDFContext
. Il disegno in FORMATO PDF è granulare per le pagine. Ogni pagina viene avviata chiamando BeginPDFPage
e completata chiamando EndPDFContent
, con il codice grafico tra. Inoltre, come per il disegno di immagini, il disegno PDF supportato dalla memoria usa un'origine in basso a sinistra, che può essere considerata modificando il CTM proprio come con le immagini.
Il codice seguente illustra come disegnare testo in un 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 ();
Il testo risultante viene disegnato nel PDF, che viene quindi contenuto in un NSData
oggetto che può essere salvato, caricato, inviato tramite posta elettronica e così via.
Riepilogo
In questo articolo sono state esaminate le funzionalità grafiche fornite tramite il framework di grafica core. È stato illustrato come usare Core Graphics per disegnare geometria, immagini e PDF all'interno del contesto di un UIView,
oggetto e nei contesti grafici supportati dalla memoria.