iOS
UIBezierPath
Sök…
Hur man applicerar hörnradie på rektanglar som ritats av UIBezierPath
Hörnradie för alla fyra kanter:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) cornerRadius: 11];
[UIColor.grayColor setFill];
[rectanglePath fill];
Hörnradie för övre vänstra kant:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) byRoundingCorners: UIRectCornerTopLeft cornerRadii: CGSizeMake(11, 11)];
[rectanglePath closePath];
[UIColor.grayColor setFill];
[rectanglePath fill];
Hörnradie för övre högra kanten:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) byRoundingCorners: UIRectCornerTopRight cornerRadii: CGSizeMake(11, 11)];
[rectanglePath closePath];
[UIColor.grayColor setFill];
[rectanglePath fill];
hörnradie för nedre vänstra kant:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) byRoundingCorners: UIRectCornerBottomLeft cornerRadii: CGSizeMake(11, 11)];
[rectanglePath closePath];
[UIColor.grayColor setFill];
[rectanglePath fill];
hörnradie för nedre högra kanten:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) byRoundingCorners: UIRectCornerBottomRight cornerRadii: CGSizeMake(11, 11)];
[rectanglePath closePath];
[UIColor.grayColor setFill];
[rectanglePath fill];
hörnradie för underkanter:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) byRoundingCorners: UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii: CGSizeMake(11, 11)];
[rectanglePath closePath];
[UIColor.grayColor setFill];
[rectanglePath fill];
hörnradie för övre kanter:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) byRoundingCorners: UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii: CGSizeMake(11, 11)];
[rectanglePath closePath];
[UIColor.grayColor setFill];
[rectanglePath fill];
Hur man skapar enkla former med UIBezierPath
För en enkel cirkel:
UIBezierPath* ovalPath = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(0,0,50,50)];
[UIColor.grayColor setFill];
[ovalPath fill];
Snabb:
let ovalPath = UIBezierPath(ovalInRect: CGRect(x: 0, y: 0, width: 50, height: 50))
UIColor.grayColor().setFill()
ovalPath.fill()
För en enkel rektangel:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRect: CGRectMake(0,0,50,50)];
[UIColor.grayColor setFill];
[rectanglePath fill];
Snabb:
let rectanglePath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 50, height: 50))
UIColor.grayColor().setFill()
rectanglePath.fill()
För en enkel linje:
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(x1,y1)];
[bezierPath addLineToPoint: CGPointMake(x2,y2)];
[UIColor.blackColor setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
Snabb:
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 en halv cirkel:
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];
Snabb:
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 en enkel triangel:
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];
Snabb:
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
För att bezier-banan ska ändras i storlek baserat på visningsramen, åsidosätter du drawRect of view som du ritar bezier-banan:
- (void)drawRect:(CGRect)frame
{
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRect: CGRectMake(CGRectGetMinX(frame), CGRectGetMinY(frame), CGRectGetWidth(frame), CGRectGetHeight(frame))];
[UIColor.grayColor setFill];
[rectanglePath fill];
}
Hur man applicerar skuggor på UIBezierPath
Tänk på en enkel rektangel som ritas av bezier-banan.
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRect: CGRectMake(x,y,width,height)];
[UIColor.grayColor setFill];
[rectanglePath fill];
Grundläggande yttre-fyllning skugga:
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);
Grundläggande inre fyllningsskugga:
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);
Designa och rita en Bezier-väg
Detta exempel visar processen från att utforma den form du vill rita den i en vy. En specifik form används men de koncept du lär kan tillämpas på valfri form.
Hur man ritar en Bézier-väg i en anpassad vy
Dessa är de viktigaste stegen:
- Designa konturen på den form du vill ha.
- Dela konturvägen i segment av linjer, bågar och kurvor.
- Bygg den vägen programmatiskt.
- Rita vägen antingen i
drawRect
eller med enCAShapeLayer
.
Designform disposition
Du kan göra vad som helst, men som ett exempel har jag valt formen nedan. Det kan vara en popup-nyckel på ett tangentbord.
Dela vägen i segment
Titta tillbaka på din formdesign och dela upp den i enklare element av linjer (för raka linjer), bågar (för cirklar och runda hörn) och kurvor (för allt annat).
Så här ser vår exempeldesign ut:
- Svart är linjesegment
- Ljusblå är bågsegment
- Röda är kurvor
- Orange prickar är styrpunkter för kurvorna
- Gröna prickar är punkterna mellan vägsegment
- Prickade linjer visar den avgränsande rektangeln
- Mörkblåa siffror är segmenten i den ordning att de kommer att läggas till programmatiskt
Bygg vägen programmatiskt
Vi börjar godtyckligt i det nedre vänstra hörnet och arbetar medsols. Jag använder rutnätet i bilden för att få x- och y-värdena för punkterna. Jag ska hårtkoda allt här, men du skulle naturligtvis inte göra det i ett riktigt projekt.
Den grundläggande processen är:
- Skapa ett nytt
UIBezierPath
- Välj en startpunkt på vägen med
moveToPoint
- Lägg till segment till sökvägen
- rad:
addLineToPoint
- arc:
addArcWithCenter
- kurva:
addCurveToPoint
- Stäng vägen med
closePath
Här är koden för att skapa sökvägen i bilden ovan.
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
}
Obs: Vissa av ovanstående kod kan minskas genom att lägga till en rad och en båge i ett enda kommando (eftersom bågen har en underförstådd startpunkt). Se här för mer information.
Rita vägen
Vi kan rita vägen antingen i ett lager eller i drawRect
.
Metod 1: Rita väg i ett lager
Vår anpassade klass ser ut så här. Vi lägger till vår Bezier-väg till en ny CAShapeLayer
när vyn initieras.
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
}
}
Och skapa vår vy i View Controller så här
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)
}
Vi får...
Hmm, det är lite litet eftersom jag hårdkodade alla siffror i. Jag kan dock väga upp storleken, så här:
let path = createBezierPath()
let scale = CGAffineTransformMakeScale(2, 2)
path.applyTransform(scale)
shapeLayer.path = path.CGPath
Metod 2: Rita väg i drawRect
Att använda drawRect
är långsammare än att rita till lagret, så detta är inte den rekommenderade metoden om du inte behöver det.
Här är den reviderade koden för vår anpassade vy:
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
}
}
vilket ger oss samma resultat ...
Ytterligare studier
Utmärkta artiklar för att förstå Bezier-vägar.
- Tänker som en Bézier-väg (allt jag någonsin har läst från den här författaren är bra och inspiration till mitt exempel härifrån kom härifrån.)
- Coding Math: Avsnitt 19 - Bezier Curves (underhållande och bra visuella illustrationer)
- Bezier Curves (hur de används i grafikapplikationer)
- Bezier Curves (bra beskrivning av hur de matematiska formlerna härleds)
anteckningar
- Detta exempel kommer ursprungligen från detta Stack Overflow-svar .
- I dina faktiska projekt bör du förmodligen inte använda hårtkodade nummer, utan snarare få storleken från din syngräns.
pajvy & kolumnvy med 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() } }