Alles, was Sie brauchen, finden Sie im Quartz 2D-Programmierhandbuch . Ich schlage vor, Sie schauen es durch.
Es kann jedoch schwierig sein, alles zusammenzusetzen, also werde ich Sie durch das Ganze führen. Wir schreiben eine Funktion, die eine Größe annimmt und ein Bild zurückgibt, das ungefähr wie eines Ihrer Segmente aussieht:
Wir starten die Funktionsdefinition folgendermaßen:
static UIImage *imageWithSize(CGSize size) {
Wir brauchen eine Konstante für die Dicke des Segments:
static CGFloat const kThickness = 20;
und eine Konstante für die Breite der Linie, die das Segment umreißt:
static CGFloat const kLineWidth = 1;
und eine Konstante für die Größe des Schattens:
static CGFloat const kShadowWidth = 8;
Als nächstes müssen wir einen Bildkontext erstellen, in dem gezeichnet werden soll:
UIGraphicsBeginImageContextWithOptions(size, NO, 0); {
Ich habe eine linke Klammer am Ende dieser Zeile angebracht, weil ich eine zusätzliche Einrückungsstufe mag, um mich daran zu erinnern, UIGraphicsEndImageContext
später anzurufen .
Da viele der Funktionen, die wir aufrufen müssen, Core Graphics-Funktionen (auch bekannt als Quartz 2D) und keine UIKit-Funktionen sind, müssen wir Folgendes erhalten CGContext
:
CGContextRef gc = UIGraphicsGetCurrentContext();
Jetzt sind wir bereit, richtig loszulegen. Zuerst fügen wir dem Pfad einen Bogen hinzu. Der Bogen verläuft entlang der Mitte des Segments, das wir zeichnen möchten:
CGContextAddArc(gc, size.width / 2, size.height / 2,
(size.width - kThickness - kLineWidth) / 2,
-M_PI / 4, -3 * M_PI / 4, YES);
Jetzt werden wir Core Graphics bitten, den Pfad durch eine "gestrichene" Version zu ersetzen, die den Pfad umreißt. Wir setzen zuerst die Dicke des Strichs auf die Dicke, die das Segment haben soll:
CGContextSetLineWidth(gc, kThickness)
und wir setzen den Linienkappenstil auf "Hintern", damit wir quadratische Enden haben :
CGContextSetLineCap(gc, kCGLineCapButt)
Dann können wir Core Graphics bitten, den Pfad durch eine gestrichene Version zu ersetzen:
CGContextReplacePathWithStrokedPath(gc)
Um diesen Pfad mit einem linearen Farbverlauf zu füllen, müssen wir Core Graphics anweisen, alle Operationen im Inneren des Pfads zu befestigen. Dadurch wird der Pfad durch Core Graphics zurückgesetzt, der Pfad wird jedoch später benötigt, um die schwarze Linie um den Rand zu zeichnen. Also kopieren wir den Pfad hier:
CGPathRef path = CGContextCopyPath(gc);
Da das Segment einen Schatten werfen soll, legen wir die Schattenparameter fest, bevor wir zeichnen:
CGContextSetShadowWithColor(gc,
CGSizeMake(0, kShadowWidth / 2), kShadowWidth / 2,
[UIColor colorWithWhite:0 alpha:0.3].CGColor);
Wir werden das Segment sowohl füllen (mit einem Farbverlauf) als auch streichen (um den schwarzen Umriss zu zeichnen). Wir wollen einen einzigen Schatten für beide Operationen. Wir teilen Core Graphics dies mit, indem wir eine Transparenzebene beginnen:
CGContextBeginTransparencyLayer(gc, 0)
Ich habe eine linke Klammer am Ende dieser Zeile angebracht, weil ich eine zusätzliche Einrückungsstufe haben möchte, um mich daran zu erinnern, CGContextEndTransparencyLayer
später anzurufen .
Da wir den Clip-Bereich des Kontexts zum Füllen ändern, aber nicht schneiden möchten, wenn wir den Umriss später streichen, müssen wir den Grafikstatus speichern:
CGContextSaveGState(gc)
Ich habe eine linke Klammer am Ende dieser Zeile angebracht, weil ich eine zusätzliche Einrückungsstufe haben möchte, um mich daran zu erinnern, CGContextRestoreGState
später anzurufen .
Um den Pfad mit einem Verlauf zu füllen, müssen wir ein Verlaufsobjekt erstellen:
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB()
CGGradientRef gradient = CGGradientCreateWithColors(rgb, (__bridge CFArrayRef)@[
(__bridge id)[UIColor grayColor].CGColor,
(__bridge id)[UIColor whiteColor].CGColor
], (CGFloat[]){ 0.0f, 1.0f })
CGColorSpaceRelease(rgb)
Wir müssen auch einen Startpunkt und einen Endpunkt für den Gradienten herausfinden. Wir werden den Pfadbegrenzungsrahmen verwenden:
CGRect bbox = CGContextGetPathBoundingBox(gc);
CGPoint start = bbox.origin;
CGPoint end = CGPointMake(CGRectGetMaxX(bbox), CGRectGetMaxY(bbox));
und wir erzwingen, dass der Farbverlauf entweder horizontal oder vertikal gezeichnet wird, je nachdem, welcher Zeitraum länger ist:
if (bbox.size.width > bbox.size.height) {
end.y = start.y;
} else {
end.x = start.x;
}
Jetzt haben wir endlich alles, was wir brauchen, um den Farbverlauf zu zeichnen. Zuerst schneiden wir auf den Pfad:
CGContextClip(gc)
Dann zeichnen wir den Farbverlauf:
CGContextDrawLinearGradient(gc, gradient, start, end, 0);
Dann können wir den Farbverlauf freigeben und den gespeicherten Grafikstatus wiederherstellen:
CGGradientRelease(gradient);
} CGContextRestoreGState(gc);
Als wir anriefen CGContextClip
, setzte Core Graphics den Pfad des Kontexts zurück. Der Pfad ist nicht Teil des gespeicherten Grafikstatus. Deshalb haben wir früher eine Kopie gemacht. Jetzt ist es Zeit, diese Kopie zu verwenden, um den Pfad im Kontext erneut festzulegen:
CGContextAddPath(gc, path)
CGPathRelease(path)
Jetzt können wir den Pfad streichen, um den schwarzen Umriss des Segments zu zeichnen:
CGContextSetLineWidth(gc, kLineWidth);
CGContextSetLineJoin(gc, kCGLineJoinMiter);
[[UIColor blackColor] setStroke];
CGContextStrokePath(gc);
Als nächstes weisen wir Core Graphics an, die Transparenzebene zu beenden. Dadurch wird angezeigt, was wir gezeichnet haben, und der darunter liegende Schatten wird hinzugefügt:
} CGContextEndTransparencyLayer(gc);
Jetzt sind wir alle mit dem Zeichnen fertig. Wir bitten UIKit, ein UIImage
aus dem Bildkontext zu erstellen , dann den Kontext zu zerstören und das Bild zurückzugeben:
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
Sie finden den Code alle zusammen in dieser Übersicht .