iOS
UIBezierPath
Suche…
Wie wird ein Eckenradius auf von UIBezierPath gezeichnete Rechtecke angewendet?
Eckenradius für alle 4 Kanten:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) cornerRadius: 11];
[UIColor.grayColor setFill];
[rectanglePath fill];
Eckenradius für oberen linken Rand:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) byRoundingCorners: UIRectCornerTopLeft cornerRadii: CGSizeMake(11, 11)];
[rectanglePath closePath];
[UIColor.grayColor setFill];
[rectanglePath fill];
Eckenradius für oberen rechten Rand:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) byRoundingCorners: UIRectCornerTopRight cornerRadii: CGSizeMake(11, 11)];
[rectanglePath closePath];
[UIColor.grayColor setFill];
[rectanglePath fill];
Eckenradius für linken unteren Rand:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) byRoundingCorners: UIRectCornerBottomLeft cornerRadii: CGSizeMake(11, 11)];
[rectanglePath closePath];
[UIColor.grayColor setFill];
[rectanglePath fill];
Eckenradius für rechten unteren Rand:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) byRoundingCorners: UIRectCornerBottomRight cornerRadii: CGSizeMake(11, 11)];
[rectanglePath closePath];
[UIColor.grayColor setFill];
[rectanglePath fill];
Eckenradius für Unterkanten:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) byRoundingCorners: UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii: CGSizeMake(11, 11)];
[rectanglePath closePath];
[UIColor.grayColor setFill];
[rectanglePath fill];
Eckenradius für Oberkanten:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) byRoundingCorners: UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii: CGSizeMake(11, 11)];
[rectanglePath closePath];
[UIColor.grayColor setFill];
[rectanglePath fill];
So erstellen Sie einfache Formen mit UIBezierPath
Für einen einfachen Kreis:
UIBezierPath* ovalPath = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(0,0,50,50)];
[UIColor.grayColor setFill];
[ovalPath fill];
Schnell:
let ovalPath = UIBezierPath(ovalInRect: CGRect(x: 0, y: 0, width: 50, height: 50))
UIColor.grayColor().setFill()
ovalPath.fill()
Für ein einfaches Rechteck:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRect: CGRectMake(0,0,50,50)];
[UIColor.grayColor setFill];
[rectanglePath fill];
Schnell:
let rectanglePath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 50, height: 50))
UIColor.grayColor().setFill()
rectanglePath.fill()
Für eine einfache Leitung:
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(x1,y1)];
[bezierPath addLineToPoint: CGPointMake(x2,y2)];
[UIColor.blackColor setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
Schnell:
let bezierPath = UIBezierPath()
bezierPath.moveToPoint(CGPoint(x: x1, y: y1))
bezierPath.addLineToPoint(CGPoint(x: x2, y: y2))
UIColor.blackColor().setStroke()
bezierPath.lineWidth = 1
bezierPath.stroke()
Für einen halben Kreis:
CGRect ovalRect = CGRectMake(x,y,width,height);
UIBezierPath* ovalPath = [UIBezierPath bezierPath];
[ovalPath addArcWithCenter: CGPointMake(0, 0) radius: CGRectGetWidth(ovalRect) / 2 startAngle: 180 * M_PI/180 endAngle: 0 * M_PI/180 clockwise: YES];
[ovalPath addLineToPoint: CGPointMake(0, 0)];
[ovalPath closePath];
CGAffineTransform ovalTransform = CGAffineTransformMakeTranslation(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect));
ovalTransform = CGAffineTransformScale(ovalTransform, 1, CGRectGetHeight(ovalRect) / CGRectGetWidth(ovalRect));
[ovalPath applyTransform: ovalTransform];
[UIColor.grayColor setFill];
[ovalPath fill];
Schnell:
let ovalRect = CGRect(x: 0, y: 0, width: 50, height: 50)
let ovalPath = UIBezierPath()
ovalPath.addArcWithCenter(CGPoint.zero, radius: ovalRect.width / 2, startAngle: 180 * CGFloat(M_PI)/180, endAngle: 0 * CGFloat(M_PI)/180, clockwise: true)
ovalPath.addLineToPoint(CGPoint.zero)
ovalPath.closePath()
var ovalTransform = CGAffineTransformMakeTranslation(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect))
ovalTransform = CGAffineTransformScale(ovalTransform, 1, ovalRect.height / ovalRect.width)
ovalPath.applyTransform(ovalTransform)
UIColor.grayColor().setFill()
ovalPath.fill()
Für ein einfaches Dreieck:
UIBezierPath* polygonPath = [UIBezierPath bezierPath];
[polygonPath moveToPoint: CGPointMake(x1, y1)];
[polygonPath addLineToPoint: CGPointMake(x2, y2)];
[polygonPath addLineToPoint: CGPointMake(x3, y2)];
[polygonPath closePath];
[UIColor.grayColor setFill];
[polygonPath fill];
Schnell:
let polygonPath = UIBezierPath()
polygonPath.moveToPoint(CGPoint(x: x1, y: y1))
polygonPath.addLineToPoint(CGPoint(x: x2, y: y2))
polygonPath.addLineToPoint(CGPoint(x: x3, y: y3))
polygonPath.closePath()
UIColor.grayColor().setFill()
polygonPath.fill()
UIBezierPath + AutoLayout
Um die Größe des Bezier-Pfads basierend auf dem Ansichtsrahmen zu ändern, überschreiben Sie die drawRect-Ansicht, die Sie den Bezier-Pfad zeichnen:
- (void)drawRect:(CGRect)frame
{
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRect: CGRectMake(CGRectGetMinX(frame), CGRectGetMinY(frame), CGRectGetWidth(frame), CGRectGetHeight(frame))];
[UIColor.grayColor setFill];
[rectanglePath fill];
}
So wenden Sie Schatten auf UIBezierPath an
Betrachten Sie ein einfaches Rechteck, das vom Bezier-Pfad gezeichnet wird.
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRect: CGRectMake(x,y,width,height)];
[UIColor.grayColor setFill];
[rectanglePath fill];
Grundlegender Außenschatten:
CGContextRef context = UIGraphicsGetCurrentContext();
NSShadow* shadow = [[NSShadow alloc] init];
[shadow setShadowColor: UIColor.blackColor];
[shadow setShadowOffset: CGSizeMake(7.1, 5.1)];
[shadow setShadowBlurRadius: 5];
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRect: CGRectMake(x,y,width,height)];
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, shadow.shadowOffset, shadow.shadowBlurRadius, [shadow.shadowColor CGColor]);
[UIColor.grayColor setFill];
[rectanglePath fill];
CGContextRestoreGState(context);
Grundfüllender Innenschatten:
CGContextRef context = UIGraphicsGetCurrentContext();
NSShadow* shadow = [[NSShadow alloc] init];
[shadow setShadowColor: UIColor.blackColor];
[shadow setShadowOffset: CGSizeMake(9.1, -7.1)];
[shadow setShadowBlurRadius: 6];
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRect: CGRectMake(x,y,width,height)];
[UIColor.grayColor setFill];
[rectanglePath fill];
CGContextSaveGState(context);
UIRectClip(rectanglePath.bounds);
CGContextSetShadowWithColor(context, CGSizeZero, 0, NULL);
CGContextSetAlpha(context, CGColorGetAlpha([shadow.shadowColor CGColor]));
CGContextBeginTransparencyLayer(context, NULL);
{
UIColor* opaqueShadow = [shadow.shadowColor colorWithAlphaComponent: 1];
CGContextSetShadowWithColor(context, shadow.shadowOffset, shadow.shadowBlurRadius, [opaqueShadow CGColor]);
CGContextSetBlendMode(context, kCGBlendModeSourceOut);
CGContextBeginTransparencyLayer(context, NULL);
[opaqueShadow setFill];
[rectanglePath fill];
CGContextEndTransparencyLayer(context);
}
CGContextEndTransparencyLayer(context);
CGContextRestoreGState(context);
Entwerfen und Zeichnen eines Bezierpfads
Dieses Beispiel zeigt den Prozess vom Entwerfen der Form, die Sie in einer Ansicht zeichnen möchten. Eine bestimmte Form wird verwendet, aber die Konzepte, die Sie lernen, können auf jede Form angewendet werden.
So zeichnen Sie einen Bézier-Pfad in einer benutzerdefinierten Ansicht
Dies sind die Hauptschritte:
- Gestalten Sie den Umriss der gewünschten Form.
- Teilen Sie den Umrisspfad in Segmente von Linien, Bögen und Kurven.
- Bauen Sie diesen Pfad programmgesteuert auf.
- Zeichnen Sie den Pfad entweder in
drawRect
oder mit einemCAShapeLayer
.
Umriss der Form gestalten
Sie können alles tun, aber als Beispiel habe ich die Form unten ausgewählt. Es könnte sich um eine Popup-Taste auf einer Tastatur handeln.
Teilen Sie den Pfad in Segmente
Sehen Sie sich Ihr Formdesign an und unterteilen Sie es in einfachere Linienelemente (für gerade Linien), Bögen (für Kreise und runde Ecken) und Kurven (für alles andere).
So würde unser Beispieldesign aussehen:
- Schwarz sind Liniensegmente
- Hellblau sind Bogensegmente
- Rot sind Kurven
- Orange Punkte sind die Kontrollpunkte für die Kurven
- Grüne Punkte sind die Punkte zwischen Pfadsegmenten
- Gepunktete Linien zeigen das Begrenzungsrechteck
- Dunkelblaue Zahlen sind die Segmente in der Reihenfolge, in der sie programmgesteuert hinzugefügt werden
Bauen Sie den Pfad programmgesteuert auf
Wir beginnen willkürlich in der unteren linken Ecke und arbeiten im Uhrzeigersinn. Ich verwende das Raster im Bild, um die x- und y-Werte für die Punkte zu erhalten. Ich werde hier alles hartcodieren, aber das würde man in einem echten Projekt natürlich nicht tun.
Der grundlegende Prozess ist:
- Erstellen Sie einen neuen
UIBezierPath
- Wählen Sie mit
moveToPoint
einen Startpunkt auf dem Pfad - Fügen Sie dem Pfad Segmente hinzu
- Zeile:
addLineToPoint
- arc:
addArcWithCenter
- Kurve:
addCurveToPoint
- Schließen Sie den Pfad mit
closePath
Hier ist der Code für den Pfad im obigen Bild.
func createBezierPath() -> UIBezierPath {
// create a new path
let path = UIBezierPath()
// starting point for the path (bottom left)
path.moveToPoint(CGPoint(x: 2, y: 26))
// *********************
// ***** Left side *****
// *********************
// segment 1: line
path.addLineToPoint(CGPoint(x: 2, y: 15))
// segment 2: curve
path.addCurveToPoint(CGPoint(x: 0, y: 12), // ending point
controlPoint1: CGPoint(x: 2, y: 14),
controlPoint2: CGPoint(x: 0, y: 14))
// segment 3: line
path.addLineToPoint(CGPoint(x: 0, y: 2))
// *********************
// ****** Top side *****
// *********************
// segment 4: arc
path.addArcWithCenter(CGPoint(x: 2, y: 2), // center point of circle
radius: 2, // this will make it meet our path line
startAngle: CGFloat(M_PI), // π radians = 180 degrees = straight left
endAngle: CGFloat(3*M_PI_2), // 3π/2 radians = 270 degrees = straight up
clockwise: true) // startAngle to endAngle goes in a clockwise direction
// segment 5: line
path.addLineToPoint(CGPoint(x: 8, y: 0))
// segment 6: arc
path.addArcWithCenter(CGPoint(x: 8, y: 2),
radius: 2,
startAngle: CGFloat(3*M_PI_2), // straight up
endAngle: CGFloat(0), // 0 radians = straight right
clockwise: true)
// *********************
// ***** Right side ****
// *********************
// segment 7: line
path.addLineToPoint(CGPoint(x: 10, y: 12))
// segment 8: curve
path.addCurveToPoint(CGPoint(x: 8, y: 15), // ending point
controlPoint1: CGPoint(x: 10, y: 14),
controlPoint2: CGPoint(x: 8, y: 14))
// segment 9: line
path.addLineToPoint(CGPoint(x: 8, y: 26))
// *********************
// **** Bottom side ****
// *********************
// segment 10: line
path.closePath() // draws the final line to close the path
return path
}
Hinweis: Einige der obigen Codes können durch Hinzufügen einer Linie und eines Bogens in einem einzelnen Befehl reduziert werden (da der Bogen einen implizierten Startpunkt hat). Sehen Sie hier für weitere Details.
Zeichne den Pfad
Wir können den Pfad entweder in einer Ebene oder in drawRect
.
Methode 1: Zeichnen Sie den Pfad in einer Ebene
Unsere benutzerdefinierte Klasse sieht so aus. Wir fügen unseren Bezier-Pfad zu einem neuen CAShapeLayer
wenn die Ansicht initialisiert wird.
import UIKit
class MyCustomView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {
// Create a CAShapeLayer
let shapeLayer = CAShapeLayer()
// The Bezier path that we made needs to be converted to
// a CGPath before it can be used on a layer.
shapeLayer.path = createBezierPath().CGPath
// apply other properties related to the path
shapeLayer.strokeColor = UIColor.blueColor().CGColor
shapeLayer.fillColor = UIColor.whiteColor().CGColor
shapeLayer.lineWidth = 1.0
shapeLayer.position = CGPoint(x: 10, y: 10)
// add the new layer to our custom view
self.layer.addSublayer(shapeLayer)
}
func createBezierPath() -> UIBezierPath {
// see previous code for creating the Bezier path
}
}
Und so erstellen Sie unsere Ansicht im View Controller
override func viewDidLoad() {
super.viewDidLoad()
// create a new UIView and add it to the view controller
let myView = MyCustomView()
myView.frame = CGRect(x: 100, y: 100, width: 50, height: 50)
myView.backgroundColor = UIColor.yellowColor()
view.addSubview(myView)
}
Wir bekommen...
Hmm, das ist ein bisschen klein, weil ich alle Zahlen fest codiert habe. Ich kann die Pfadgröße jedoch wie folgt vergrößern:
let path = createBezierPath()
let scale = CGAffineTransformMakeScale(2, 2)
path.applyTransform(scale)
shapeLayer.path = path.CGPath
Methode 2: Zeichnen Sie den Pfad in drawRect
Die Verwendung von drawRect
ist langsamer als das Zeichnen auf die Ebene. drawRect
wird diese Methode nicht empfohlen, wenn Sie sie nicht benötigen.
Hier ist der überarbeitete Code für unsere benutzerdefinierte Ansicht:
import UIKit
class MyCustomView: UIView {
override func drawRect(rect: CGRect) {
// create path (see previous code)
let path = createBezierPath()
// fill
let fillColor = UIColor.whiteColor()
fillColor.setFill()
// stroke
path.lineWidth = 1.0
let strokeColor = UIColor.blueColor()
strokeColor.setStroke()
// Move the path to a new location
path.applyTransform(CGAffineTransformMakeTranslation(10, 10))
// fill and stroke the path (always do these last)
path.fill()
path.stroke()
}
func createBezierPath() -> UIBezierPath {
// see previous code for creating the Bezier path
}
}
was uns das gleiche Ergebnis gibt ...
Weitere Studie
Hervorragende Artikel zum Verständnis der Bezierpfade.
- Denken wie ein Bézier-Pfad (Alles, was ich je von diesem Autor gelesen habe, ist gut und die Inspiration für mein Beispiel oben stammt von hier.)
- Kodierung Mathematik: Episode 19 - Bezierkurven (unterhaltsame und gute visuelle Illustrationen)
- Bezierkurven (wie sie in Grafikanwendungen verwendet werden)
- Bezierkurven (gute Beschreibung der Herleitung der mathematischen Formeln)
Anmerkungen
- Dieses Beispiel stammt ursprünglich aus dieser Stack Overflow-Antwort .
- In Ihren aktuellen Projekten sollten Sie wahrscheinlich keine fest codierten Zahlen verwenden, sondern die Größenangaben aus den Grenzen Ihrer Ansicht ziehen.
Kreisansicht und Spaltenansicht mit UIBezierPath
- (void)drawRect:(CGRect)rect { NSArray *data = @[@30, @15, @5, @17, @3, @10, @20]; // 1. context CGContextRef cxtRef = UIGraphicsGetCurrentContext(); CGPoint center = CGPointMake(150, 150); CGFloat radius = 150; __block CGFloat startAngle = 0; [data enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { // 2. create path CGFloat endAngle = obj.floatValue / 100 * M_PI * 2 + startAngle; UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; [circlePath addLineToPoint:center]; // 3. add path CGContextAddPath(cxtRef, circlePath.CGPath); // set color [[UIColor colorWithRed:((float)arc4random_uniform(256) / 255.0) green:((float)arc4random_uniform(256) / 255.0) blue:((float)arc4random_uniform(256) / 255.0) alpha:1.0] setFill]; // 4. render CGContextDrawPath(cxtRef, kCGPathFill); // reset angle startAngle = endAngle; }]; }
override func draw(_ rect: CGRect) { // define data to create pie chart let data: [Int] = [30, 15, 5, 17, 3, 10, 20] // 1. find center of draw rect let center: CGPoint = CGPoint(x: rect.midX, y: rect.midY) // 2. calculate radius of pie let radius = min(rect.width, rect.height) / 2.0 var startAngle: CGFloat = 0.0 for value in data { // 3. calculate end angle for slice let endAngle = CGFloat(value) / 100.0 * CGFloat.pi * 2.0 + startAngle // 4. create UIBezierPath for slide let circlePath = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true) // 5. add line to center to close path circlePath.addLine(to: center) // 6. set fill color for current slice UIColor(red: (CGFloat(arc4random_uniform(256)) / 255.0), green: (CGFloat(arc4random_uniform(256)) / 255.0), blue: (CGFloat(arc4random_uniform(256)) / 255.0), alpha: 1.0).setFill() // 7. fill slice path circlePath.fill() // 8. set end angle as start angle for next slice startAngle = endAngle } }
- (void)drawRect:(CGRect)rect { NSArray *data = @[@300, @150.65, @55.3, @507.7, @95.8, @700, @650.65]; // 1. CGContextRef cxtRef = UIGraphicsGetCurrentContext(); NSInteger columnCount = 7; CGFloat width = self.bounds.size.width / (columnCount + columnCount - 1); for (NSInteger i = 0; i < columnCount; i++) { // 2. CGFloat height = [data[i] floatValue] / 1000 * self.bounds.size.height; // floatValue CGFloat x = 0 + width * (2 * i); CGFloat y = self.bounds.size.height - height; UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:CGRectMake(x, y, width, height)]; CGContextAddPath(cxtRef, rectPath.CGPath); // 3. [[UIColor colorWithRed:((float)arc4random_uniform(256) / 255.0) green:((float)arc4random_uniform(256) / 255.0) blue:((float)arc4random_uniform(256) / 255.0) alpha:1.0] setFill]; CGContextDrawPath(cxtRef, kCGPathFill); } }
override func draw(_ rect: CGRect) { // define data for chart let data: [CGFloat] = [300, 150.65, 55.3, 507.7, 95.8, 700, 650.65] // 1. calculate number of columns let columnCount = data.count // 2. calculate column width let columnWidth = rect.width / CGFloat(columnCount + columnCount - 1) for (columnIndex, value) in data.enumerated() { // 3. calculate column height let columnHeight = value / 1000.0 * rect.height // 4. calculate column origin let columnOrigin = CGPoint(x: (columnWidth * 2.0 * CGFloat(columnIndex)), y: (rect.height - columnHeight)) // 5. create path for column let columnPath = UIBezierPath(rect: CGRect(origin: columnOrigin, size: CGSize(width: columnWidth, height: columnHeight))) // 6. set fill color for current column UIColor(red: (CGFloat(arc4random_uniform(256)) / 255.0), green: (CGFloat(arc4random_uniform(256)) / 255.0), blue: (CGFloat(arc4random_uniform(256)) / 255.0), alpha: 1.0).setFill() // 7. fill column path columnPath.fill() } }