Condividi tramite


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:

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 Drawdi :

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:

  1. Ottenere un contesto grafico.

  2. Configurare gli attributi di disegno.

  3. Creare una geometria dalle primitive di disegno.

  4. 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 CGPathoggetto , 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:

Triangolo di output di esempio

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:

Esempio con un riempimento sfumato

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:

L'aggiunta di questo codice prima di qualsiasi operazione di disegno comporta tratti tratteggiati

Si noti che quando si usa l'API unificata in Xamarin.iOS, il tipo di matrice deve essere un nfloate 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:

Immagine disegnata a capovolto

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:

Immagine di esempio 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:

Il testo risultante viene visualizzato con l'immagine

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:

  1. UIGraphics.BeginImageContext Chiamando (o BeginImageContextWithOptions)

  2. 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 UIViewoggetto . 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 CGPDFDocumentoggetto :

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.