iOS
UIBezierPath
Szukaj…
Jak zastosować promień narożnika do prostokątów narysowanych przez UIBezierPath
Promień narożnika dla wszystkich 4 krawędzi:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) cornerRadius: 11];
[UIColor.grayColor setFill];
[rectanglePath fill];
Promień narożnika lewej górnej krawędzi:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) byRoundingCorners: UIRectCornerTopLeft cornerRadii: CGSizeMake(11, 11)];
[rectanglePath closePath];
[UIColor.grayColor setFill];
[rectanglePath fill];
Promień narożnika dla prawej górnej krawędzi:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) byRoundingCorners: UIRectCornerTopRight cornerRadii: CGSizeMake(11, 11)];
[rectanglePath closePath];
[UIColor.grayColor setFill];
[rectanglePath fill];
promień narożnika dla dolnej lewej krawędzi:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) byRoundingCorners: UIRectCornerBottomLeft cornerRadii: CGSizeMake(11, 11)];
[rectanglePath closePath];
[UIColor.grayColor setFill];
[rectanglePath fill];
promień narożnika dla prawej dolnej krawędzi:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) byRoundingCorners: UIRectCornerBottomRight cornerRadii: CGSizeMake(11, 11)];
[rectanglePath closePath];
[UIColor.grayColor setFill];
[rectanglePath fill];
promień narożnika dla dolnych krawędzi:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) byRoundingCorners: UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii: CGSizeMake(11, 11)];
[rectanglePath closePath];
[UIColor.grayColor setFill];
[rectanglePath fill];
promień narożnika dla górnych krawędzi:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) byRoundingCorners: UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii: CGSizeMake(11, 11)];
[rectanglePath closePath];
[UIColor.grayColor setFill];
[rectanglePath fill];
Jak tworzyć proste kształty za pomocą UIBezierPath
Dla prostego koła:
UIBezierPath* ovalPath = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(0,0,50,50)];
[UIColor.grayColor setFill];
[ovalPath fill];
Szybki:
let ovalPath = UIBezierPath(ovalInRect: CGRect(x: 0, y: 0, width: 50, height: 50))
UIColor.grayColor().setFill()
ovalPath.fill()
Dla prostego prostokąta:
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRect: CGRectMake(0,0,50,50)];
[UIColor.grayColor setFill];
[rectanglePath fill];
Szybki:
let rectanglePath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 50, height: 50))
UIColor.grayColor().setFill()
rectanglePath.fill()
Dla prostej linii:
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(x1,y1)];
[bezierPath addLineToPoint: CGPointMake(x2,y2)];
[UIColor.blackColor setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
Szybki:
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()
Dla półkola:
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];
Szybki:
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()
Dla prostego trójkąta:
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];
Szybki:
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
Aby zmienić ścieżkę Béziera na podstawie ramki widoku, nadpisz drawRect widoku, który rysujesz ścieżką Béziera:
- (void)drawRect:(CGRect)frame
{
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRect: CGRectMake(CGRectGetMinX(frame), CGRectGetMinY(frame), CGRectGetWidth(frame), CGRectGetHeight(frame))];
[UIColor.grayColor setFill];
[rectanglePath fill];
}
Jak zastosować cienie do UIBezierPath
Rozważ prosty prostokąt rysowany ścieżką Beziera.
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRect: CGRectMake(x,y,width,height)];
[UIColor.grayColor setFill];
[rectanglePath fill];
Podstawowy cień zewnętrzny:
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);
Podstawowy cień wypełnienia wewnętrznego:
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);
Projektowanie i rysowanie ścieżki Beziera
Ten przykład pokazuje proces projektowania kształtu, który chcesz narysować na widoku. Używany jest określony kształt, ale poznanych pojęć można zastosować do dowolnego kształtu.
Jak narysować ścieżkę Béziera w widoku niestandardowym
Oto główne kroki:
- Zaprojektuj kontur pożądanego kształtu.
- Podziel ścieżkę konturu na segmenty linii, łuków i krzywych.
- Zbuduj tę ścieżkę programowo.
- Narysuj ścieżkę w
drawRectlub za pomocąCAShapeLayer.
Zarys kształtu projektu
Możesz zrobić wszystko, ale jako przykład wybrałem poniższy kształt. Może to być klawisz wyskakujący na klawiaturze.
Podziel ścieżkę na segmenty
Spójrz wstecz na swój projekt kształtu i podziel go na prostsze elementy linii (dla linii prostych), łuków (dla okręgów i okrągłych narożników) i krzywych (dla czegokolwiek innego).
Oto jak wyglądałby nasz przykładowy projekt:
- Czarne to odcinki linii
- Jasnoniebieskie to segmenty łukowe
- Czerwone to krzywe
- Pomarańczowe kropki to punkty kontrolne dla krzywych
- Zielone kropki to punkty między segmentami ścieżki
- Kropkowane linie pokazują prostokąt ograniczający
- Ciemnoniebieskie liczby to segmenty w kolejności, w której zostaną dodane programowo
Zbuduj ścieżkę programowo
Dowolnie zaczniemy w lewym dolnym rogu i będziemy pracować zgodnie z ruchem wskazówek zegara. Użyję siatki na obrazie, aby uzyskać wartości xiy dla punktów. Będę tutaj kodować wszystko na sztywno, ale oczywiście nie zrobiłbyś tego w prawdziwym projekcie.
Podstawowym procesem jest:
- Utwórz nową
UIBezierPath - Wybierz punkt początkowy na ścieżce za pomocą
moveToPoint - Dodaj segmenty do ścieżki
- linia:
addLineToPoint - arc:
addArcWithCenter - krzywa:
addCurveToPoint
- Zamknij ścieżkę za pomocą
closePath
Oto kod do utworzenia ścieżki na powyższym obrazku.
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
}
Uwaga: Część powyższego kodu można zmniejszyć, dodając linię i łuk w jednym poleceniu (ponieważ łuk ma domyślny punkt początkowy). Zobacz tutaj po więcej szczegółów.
Narysuj ścieżkę
Możemy narysować ścieżkę w warstwie lub w drawRect .
Metoda 1: Narysuj ścieżkę w warstwie
Nasza klasa niestandardowa wygląda tak. Dodajemy naszą ścieżkę Beziera do nowej CAShapeLayer po zainicjowaniu widoku.
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
}
}
I tworzenie naszego widoku w kontrolerze widoku w ten sposób
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)
}
Dostajemy ...
Hmm, to trochę za małe, bo na stałe wpisałem wszystkie liczby. Mogę jednak zwiększyć rozmiar ścieżki w ten sposób:
let path = createBezierPath()
let scale = CGAffineTransformMakeScale(2, 2)
path.applyTransform(scale)
shapeLayer.path = path.CGPath
Metoda 2: Narysuj ścieżkę w drawRect
Korzystanie z drawRect jest wolniejsze niż rysowanie na warstwie, więc nie jest to zalecana metoda, jeśli jej nie potrzebujesz.
Oto poprawiony kod naszego niestandardowego widoku:
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
}
}
co daje nam ten sam wynik ...
Dalsze badanie
Doskonałe artykuły do zrozumienia ścieżek Beziera.
- Myślenie jak ścieżka Béziera (wszystko, co kiedykolwiek czytałem od tego autora, jest dobre, a inspiracja do powyższego przykładu pochodzi stąd).
- Coding Math: Episode 19 - Bezier Curves (zabawne i dobre ilustracje wizualne)
- Krzywe Beziera (jak są wykorzystywane w aplikacjach graficznych)
- Krzywe Beziera (dobry opis wyprowadzania wzorów matematycznych)
Notatki
- Ten przykład pochodzi pierwotnie z tej odpowiedzi Przepełnienie stosu .
- W swoich rzeczywistych projektach prawdopodobnie nie powinieneś używać liczb zakodowanych na stałe, ale raczej uzyskać rozmiary z granic widoku.
widok kołowy i widok kolumny z 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()
}
}





















