Szukaj…


Jak zastosować promień narożnika do prostokątów narysowanych przez UIBezierPath

Promień narożnika dla wszystkich 4 krawędzi:

wprowadź opis zdjęcia tutaj

 UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,width,height) cornerRadius: 11];
[UIColor.grayColor setFill];
[rectanglePath fill];

Promień narożnika lewej górnej krawędzi:

wprowadź opis zdjęcia tutaj

 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:

wprowadź opis zdjęcia tutaj

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:

wprowadź opis zdjęcia tutaj

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:

wprowadź opis zdjęcia tutaj

 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:

wprowadź opis zdjęcia tutaj

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:

wprowadź opis zdjęcia tutaj

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:

wprowadź opis zdjęcia tutaj

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:

wprowadź opis zdjęcia tutaj

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:

wprowadź opis zdjęcia tutaj

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:

wprowadź opis zdjęcia tutaj

 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:

wprowadź opis zdjęcia tutaj

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.

wprowadź opis zdjęcia tutaj

 UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRect: CGRectMake(x,y,width,height)];
 [UIColor.grayColor setFill];
 [rectanglePath fill];

Podstawowy cień zewnętrzny:

wprowadź opis zdjęcia tutaj

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:

wprowadź opis zdjęcia tutaj

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:

  1. Zaprojektuj kontur pożądanego kształtu.
  2. Podziel ścieżkę konturu na segmenty linii, łuków i krzywych.
  3. Zbuduj tę ścieżkę programowo.
  4. Narysuj ścieżkę w drawRect lub 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.

wprowadź opis zdjęcia tutaj

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:

wprowadź opis zdjęcia tutaj

  • 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:

  1. Utwórz nową UIBezierPath
  2. Wybierz punkt początkowy na ścieżce za pomocą moveToPoint
  3. Dodaj segmenty do ścieżki
  • linia: addLineToPoint
  • arc: addArcWithCenter
  • krzywa: addCurveToPoint
  1. 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 ...

wprowadź opis zdjęcia tutaj

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

wprowadź opis zdjęcia tutaj

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 ...

wprowadź opis zdjęcia tutaj

Dalsze badanie

Doskonałe artykuły do zrozumienia ścieżek Beziera.

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

  • widok ciasta
    widok ciasta
- (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
    }
  }
  • widok kolumny
    widok kolumny
- (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()
    }
  }


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow