Udostępnij za pośrednictwem


Core Graphics in Xamarin.iOS

W tym artykule omówiono podstawowe struktury graficzne systemu iOS. Pokazuje, jak używać grafiki Core do rysowania geometrii, obrazów i plików PDF.

System iOS zawiera strukturę Core Graphics , aby zapewnić obsługę rysunku niskiego poziomu. Te struktury umożliwiają korzystanie z zaawansowanych funkcji graficznych w zestawie UIKit.

Podstawowa grafika to nisko-poziomowa struktura grafiki 2D, która umożliwia rysowanie niezależnej grafiki urządzenia. Cały rysunek 2D w zestawie UIKit używa grafiki core wewnętrznie.

Podstawowa grafika obsługuje rysowanie w wielu scenariuszach, w tym:

Przestrzeń geometryczna

Niezależnie od scenariusza wszystkie rysunki wykonane za pomocą grafiki core są wykonywane w przestrzeni geometrycznej, co oznacza, że działa w punktach abstrakcyjnych, a nie pikselach. Opisano, co chcesz narysować pod względem geometrii i stanu rysunku, takich jak kolory, style linii itp., oraz uchwyty grafiki core przekładają wszystko na piksele. Taki stan jest dodawany do kontekstu graficznego, o którym można myśleć jak kanwa malarza.

Istnieje kilka korzyści związanych z tym podejściem:

  • Kod rysunku staje się dynamiczny i może następnie modyfikować grafikę w czasie wykonywania.
  • Zmniejszenie zapotrzebowania na obrazy statyczne w pakiecie aplikacji może zmniejszyć rozmiar aplikacji.
  • Grafika staje się bardziej odporna na zmiany rozdzielczości na różnych urządzeniach.

Rysowanie w podklasie UIView

Każda UIView z nich ma metodę wywoływaną Draw przez system, gdy musi zostać narysowana. Aby dodać kod rysunku do widoku, podklasy UIView i zastąpienia Draw:

public class TriangleView : UIView
{
    public override void Draw (CGRect rect)
    {
        base.Draw (rect);
    }
}

Rysowanie nigdy nie powinno być wywoływane bezpośrednio. Jest on wywoływany przez system podczas przetwarzania pętli przebiegu. Po pierwszym przejściu przez pętlę uruchamiania po dodaniu widoku do hierarchii widoków wywoływana jest jej Draw metoda. Kolejne wywołania są Draw wykonywane, gdy widok jest oznaczony jako wymagający rysowania przez wywołanie SetNeedsDisplay elementu lub SetNeedsDisplayInRect w widoku.

Wzorzec kodu graficznego

Kod w implementacji Draw powinien opisywać, czego chce ściągnięty. Kod rysunku jest zgodny ze wzorcem, w którym ustawia stan rysunku i wywołuje metodę, aby zażądać jej narysowania. Ten wzorzec można uogólnić w następujący sposób:

  1. Uzyskiwanie kontekstu graficznego.

  2. Konfigurowanie atrybutów rysunku.

  3. Utwórz geometrię na podstawie elementów pierwotnych rysunku.

  4. Wywołaj metodę Draw lub Stroke.

Przykład rysunku podstawowego

Rozważmy na przykład następujący fragment kodu:

//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);
}

Podzielmy ten kod:

using (CGContext g = UIGraphics.GetCurrentContext ()) {
...
}

W tym wierszu najpierw pobiera bieżący kontekst graficzny do użycia do rysowania. Kontekst graficzny można traktować jako kanwę, na której odbywa się rysunek, zawierający cały stan rysunku, taki jak pociągnięcie i kolory wypełnienia, a także geometria do narysowania.

g.SetLineWidth (10);
UIColor.Blue.SetFill ();
UIColor.Red.SetStroke ();

Po uzyskaniu kontekstu graficznego kod konfiguruje niektóre atrybuty do użycia podczas rysowania, jak pokazano powyżej. W tym przypadku ustawiono szerokość linii, pociągnięcie i kolory wypełnienia. Każdy kolejny rysunek będzie następnie używać tych atrybutów, ponieważ są one zachowywane w stanie kontekstu grafiki.

Aby utworzyć geometrię CGPath, kod używa elementu , który umożliwia opisanie ścieżki grafiki z linii i krzywych. W tym przypadku ścieżka dodaje linie łączące tablicę punktów, aby utworzyć trójkąt. Jak pokazano poniżej Core Graphics używa układu współrzędnych do rysowania widoku, gdzie początek znajduje się w lewym górnym rogu, z dodatnim x-direct w prawo i kierunku dodatnim y w dół:

var path = new CGPath ();

path.AddLines (new CGPoint[]{
new CGPoint (100, 200),
new CGPoint (160, 100),
new CGPoint (220, 200)});

path.CloseSubpath ();

Po utworzeniu ścieżki zostanie ona dodana do kontekstu graficznego, aby wywołanie AddPath i DrawPath odpowiednio je narysować.

Wynikowy widok jest pokazany poniżej:

Przykładowy trójkąt wyjściowy

Tworzenie wypełnień gradientowych

Dostępne są również bogatsze formy rysunku. Na przykład podstawowa grafika umożliwia tworzenie wypełnień gradientowych i stosowanie ścieżek wycinków. Aby narysować wypełnienie gradientowe wewnątrz ścieżki z poprzedniego przykładu, najpierw należy ustawić ścieżkę jako ścieżkę wycinkową:

// 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 ();

Ustawienie bieżącej ścieżki jako ograniczeń ścieżki przycinania wszystkich kolejnych rysunków w geometrii ścieżki, takich jak następujący kod, który rysuje gradient liniowy:

// 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);
    }

Te zmiany powodują wypełnienie gradientowe, jak pokazano poniżej:

Przykład z wypełnieniem gradientowym

Modyfikowanie wzorców linii

Atrybuty rysunku linii można również modyfikować za pomocą grafiki core. Obejmuje to zmianę szerokości linii i koloru pociągnięcia, a także samego wzorca linii, jak pokazano w poniższym kodzie:

//use a dashed line
g.SetLineDash (0, new nfloat[] { 10, 4 * (nfloat)Math.PI });

Dodanie tego kodu przed wszelkimi operacjami rysunkowymi powoduje, że kreskowane pociągnięcia mają długość 10 jednostek, z 4 jednostkami odstępów między kreskami, jak pokazano poniżej:

Dodanie tego kodu przed wykonaniem jakichkolwiek operacji rysowania powoduje kreskowane pociągnięcia

Należy pamiętać, że w przypadku korzystania z ujednoliconego interfejsu API w środowisku Xamarin.iOS typ tablicy musi być typu nfloat, a także musi być jawnie rzutowany na math.PI.

Rysowanie obrazów i tekstu

Oprócz ścieżek rysunkowych w kontekście graficznym widoku podstawowa grafika obsługuje również rysowanie obrazów i tekstu. Aby narysować obraz, wystarczy utworzyć obiekt CGImage i przekazać go do wywołania DrawImage :

public override void Draw (CGRect rect)
{
    base.Draw (rect);

    using(CGContext g = UIGraphics.GetCurrentContext ()){
        g.DrawImage (rect, UIImage.FromFile ("MyImage.png").CGImage);
    }
}

Jednak powoduje to utworzenie obrazu narysowanego do góry nogami, jak pokazano poniżej:

Obraz narysowany do góry nogami

Przyczyną tego jest pochodzenie podstawowego grafiki dla rysunku obrazu znajduje się w lewym dolnym rogu, a widok ma swoje źródło w lewym górnym rogu. W związku z tym, aby poprawnie wyświetlić obraz, należy zmodyfikować źródło, co można osiągnąć, modyfikując bieżącą macierz transformacji (CTM). Funkcja CTM definiuje lokalizację punktów na żywo, znaną również jako przestrzeń użytkownika. Odwrócenie ctM w kierunku y i przesunięcie go przez wysokość granic w ujemnym kierunku y może przerzucić obraz.

Kontekst graficzny zawiera metody pomocnicze do przekształcania funkcji CTM. W tym przypadku ScaleCTM "przerzuca" rysunek i TranslateCTM przesuwa go do lewego górnego rogu, jak pokazano poniżej:

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);
}

Wynikowy obraz jest następnie wyświetlany w pozycji pionowej:

Przykładowy obraz wyświetlany w pozycji pionowej

Ważne

Zmiany kontekstu grafiki mają zastosowanie do wszystkich kolejnych operacji rysowania. W związku z tym po przekształceniu funkcji CTM będzie ona mieć wpływ na każdy dodatkowy rysunek. Jeśli na przykład narysujesz trójkąt po przekształceniu CTM, będzie on wyświetlany do góry nogami.

Dodawanie tekstu do obrazu

Podobnie jak w przypadku ścieżek i obrazów, rysowanie tekstu z grafiką Core wymaga tego samego podstawowego wzorca ustawiania stanu grafiki i wywoływania metody do rysowania. W przypadku tekstu metoda wyświetlania tekstu to ShowText. Po dodaniu do przykładu rysunku obrazu poniższy kod rysuje jakiś tekst przy użyciu grafiki Core:

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");
}

Jak widać, ustawienie stanu grafiki dla rysunku tekstowego jest podobne do geometrii rysunku. W przypadku rysowania tekstu stosuje się również tryb rysowania tekstu i czcionkę. W tym przypadku stosowany jest również cień, chociaż stosowanie cieni działa tak samo w przypadku rysunku ścieżki.

Wynikowy tekst jest wyświetlany z obrazem, jak pokazano poniżej:

Wynikowy tekst jest wyświetlany z obrazem

Obrazy oparte na pamięci

Oprócz rysowania do kontekstu graficznego widoku core graphics obsługuje obrazy oparte na pamięci rysunku, znane również jako rysowanie poza ekranem. Wymaga to następujących czynności:

  • Tworzenie kontekstu graficznego, który jest wspierany przez mapę bitową w pamięci
  • Ustawianie stanu rysunku i wydawanie poleceń rysunku
  • Pobieranie obrazu z kontekstu
  • Usuwanie kontekstu

Draw W przeciwieństwie do metody, w której kontekst jest dostarczany przez widok, w tym przypadku kontekst jest tworzony na jeden z dwóch sposobów:

  1. Wywołując ( UIGraphics.BeginImageContext lub BeginImageContextWithOptions)

  2. Tworząc nowy CGBitmapContextInstance

CGBitmapContextInstance jest przydatna podczas bezpośredniej pracy z bitami obrazów, na przykład w przypadkach, w których używasz niestandardowego algorytmu manipulowania obrazami. We wszystkich innych przypadkach należy użyć polecenia BeginImageContext lub BeginImageContextWithOptions.

Po utworzeniu kontekstu obrazu dodawanie kodu rysunku jest tak samo jak w podklasie UIView . Na przykład przykład kodu użyty wcześniej do narysowania trójkąta może służyć do rysowania obrazu w pamięci zamiast w elemecie UIView, jak pokazano poniżej:

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;
}

Typowym zastosowaniem rysunku do mapy bitowej opartej na pamięci jest przechwycenie obrazu z dowolnego UIViewelementu . Na przykład poniższy kod renderuje warstwę widoku do kontekstu mapy bitowej i tworzy z niego element 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 ();

Rysowanie plików PDF

Oprócz obrazów podstawowa grafika obsługuje rysowanie w formacie PDF. Podobnie jak obrazy, możesz renderować plik PDF w pamięci, a także odczytywać plik PDF do renderowania w pliku UIView.

Plik PDF w widoku interfejsu użytkownika

Podstawowa grafika obsługuje również odczytywanie pliku PDF z pliku i renderowanie go w widoku przy użyciu CGPDFDocument klasy . Klasa CGPDFDocument reprezentuje plik PDF w kodzie i może służyć do odczytywania i rysowania stron.

Na przykład następujący kod w podklasie UIView odczytuje plik PDF z pliku do pliku 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)
    {
        ...
    }
}

Metoda Draw może następnie użyć CGPDFDocument elementu , aby odczytać stronę i CGPDFPage renderować ją, wywołując metodę DrawPDFPage, jak pokazano poniżej:

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);
        }
    }
}

Plik PDF oparty na pamięci

W przypadku pliku PDF w pamięci należy utworzyć kontekst PDF, wywołując polecenie BeginPDFContext. Rysowanie do formatu PDF jest szczegółowe dla stron. Każda strona jest uruchamiana przez wywołanie BeginPDFPage i ukończone przez wywołanie EndPDFContentmetody , z kodem graficznym między. Ponadto, podobnie jak w przypadku rysunku obrazu, rysunek PDF oparty na pamięci używa źródła w lewym dolnym rogu, co można uwzględnić przez zmodyfikowanie funkcji CTM tak jak w przypadku obrazów.

Poniższy kod pokazuje, jak rysować tekst w pliku 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 ();

Wynikowy tekst jest rysowany do pliku PDF, który jest następnie zawarty w pliku NSData , który można zapisać, przekazać, wysłać pocztą e-mail itp.

Podsumowanie

W tym artykule przyjrzeliśmy się możliwościom grafiki udostępnianym za pośrednictwem platformy Core Graphics . Zobaczyliśmy, jak używać grafiki core do rysowania geometrii, obrazów i plików PDF w kontekście elementu UIView, oraz kontekstów grafiki opartych na pamięci.