wake-up-neo.com

Zeichnen von UIBezierPath auf Code generiert UIView

Ich habe ein UIView zur Laufzeit im Code hinzugefügt.

Ich möchte ein UIBezierPath darin zeichnen, aber bedeutet dies, dass ich das drawRect für UIView überschreiben muss?

Oder gibt es eine andere Möglichkeit, auf dem benutzerdefinierten UIView zu zeichnen?

Hier ist der Code zum Generieren des UIView:

UIView* shapeView = [[UIView alloc]initWithFrame:CGRectMake(xOrigin,yOrigin+(i*MENU_BLOCK_FRAME_HEIGHT), self.shapeScroll.frame.size.width, MENU_BLOCK_FRAME_HEIGHT)];
shapeView.clipsToBounds = YES;

Und hier ist die Funktion zum Erstellen und Zurückgeben eines UIBezierPath:

- (UIBezierPath*)createPath
{
    UIBezierPath* path = [[UIBezierPath alloc]init];
    [path moveToPoint:CGPointMake(100.0, 50.0)];
    [path addLineToPoint:CGPointMake(200.0,50.0)];
    [path addLineToPoint:CGPointMake(200.0, 200.0)];
    [path addLineToPoint:CGPointMake(100.0, 200.0)];
    [path closePath];
    return path;
}
61
Itzik984

Es ist noch nicht lange her, dass ich nicht einmal wusste, wie man Bézier ausspricht, geschweige denn, wie man Bézier-Pfade verwendet, um eine benutzerdefinierte Form zu erstellen. Folgendes habe ich gelernt. Es stellt sich heraus, dass sie nicht so beängstigend sind, wie sie zunächst scheinen.

Wie zeichnet man ein Bézier-Pfad in einer benutzerdefinierten Ansicht

Dies sind die Hauptschritte:

  1. Entwerfen Sie den Umriss der gewünschten Form.
  2. Teilen Sie den Umrisspfad in Segmente aus Linien, Bögen und Kurven.
  3. Erstellen Sie diesen Pfad programmgesteuert.
  4. Zeichnen Sie den Pfad entweder in drawRect oder mit einem CAShapeLayer.

Formkontur entwerfen

Sie könnten alles tun, aber als Beispiel habe ich die Form unten gewählt. Es könnte eine Popup-Taste auf einer Tastatur sein.

enter image description here

Teilen Sie den Pfad in Segmente

Sehen Sie sich Ihr Formdesign noch einmal an und unterteilen Sie es in einfachere Elemente aus Linien (für gerade Linien), Bögen (für Kreise und runde Ecken) und Kurven (für alles andere).

So würde unser Beispieldesign aussehen:

enter image description here

  • 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 umgebende Rechteck
  • Dunkelblaue Zahlen sind die Segmente in der Reihenfolge, in der sie programmgesteuert hinzugefügt werden

Erstellen Sie den Pfad programmgesteuert

Wir werden willkürlich in der unteren linken Ecke beginnen und im Uhrzeigersinn arbeiten. Ich werde das Gitter im Bild verwenden, um die x- und y-Werte für die Punkte zu erhalten. Ich werde hier alles fest codieren, aber das würden Sie natürlich nicht in einem echten Projekt tun.

Der grundlegende Prozess ist:

  1. Erstelle ein neues UIBezierPath
  2. Wählen Sie einen Startpunkt auf dem Pfad mit moveToPoint
  3. Segmente zum Pfad hinzufügen
    • zeile: addLineToPoint
    • arc: addArcWithCenter
    • kurve: addCurveToPoint
  4. 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.move(to: CGPoint(x: 2, y: 26))

    // *********************
    // ***** Left side *****
    // *********************

    // segment 1: line
    path.addLine(to: CGPoint(x: 2, y: 15))

    // segment 2: curve
    path.addCurve(to: CGPoint(x: 0, y: 12), // ending point
        controlPoint1: CGPoint(x: 2, y: 14),
        controlPoint2: CGPoint(x: 0, y: 14))

    // segment 3: line
    path.addLine(to: CGPoint(x: 0, y: 2))

    // *********************
    // ****** Top side *****
    // *********************

    // segment 4: arc
    path.addArc(withCenter: 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.addLine(to: CGPoint(x: 8, y: 0))

    // segment 6: arc
    path.addArc(withCenter: 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.addLine(to: CGPoint(x: 10, y: 12))

    // segment 8: curve
    path.addCurve(to: CGPoint(x: 8, y: 15), // ending point
        controlPoint1: CGPoint(x: 10, y: 14),
        controlPoint2: CGPoint(x: 8, y: 14))

    // segment 9: line
    path.addLine(to: CGPoint(x: 8, y: 26))

    // *********************
    // **** Bottom side ****
    // *********************

    // segment 10: line
    path.close() // draws the final line to close the path

    return path
}

Hinweis: Ein Teil des obigen Codes kann durch Hinzufügen einer Linie und eines Bogens in einem einzigen Befehl reduziert werden (da der Bogen einen implizierten Startpunkt hat). Siehe hier für weitere Details.

Zeichne den Pfad

Wir können den Pfad entweder in einer Ebene oder in drawRect zeichnen.

Methode 1: Pfad in eine Ebene zeichnen

Unsere benutzerdefinierte Klasse sieht so aus. Wir fügen unseren Bezier-Pfad zu einem neuen CAShapeLayer hinzu, 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.blue.cgColor
        shapeLayer.fillColor = UIColor.white.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.yellow
    view.addSubview(myView)

}

Wir bekommen...

enter image description here

Hmm, das ist ein bisschen klein, weil ich alle Zahlen fest codiert habe. Ich kann die Pfadgröße jedoch wie folgt skalieren:

let path = createBezierPath()
let scale = CGAffineTransform(scaleX: 2, y: 2)
path.apply(scale)
shapeLayer.path = path.cgPath

enter image description here

Methode 2: Pfad in draw zeichnen

Die Verwendung von draw ist langsamer als das Zeichnen in die Ebene, daher ist dies nicht die empfohlene Methode, wenn Sie sie nicht benötigen.

Hier ist der überarbeitete Code für unsere benutzerdefinierte Ansicht:

import UIKit
class MyCustomView: UIView {

    override func draw(_ rect: CGRect) {

        // create path (see previous code)
        let path = createBezierPath()

        // fill
        let fillColor = UIColor.white
        fillColor.setFill()

        // stroke
        path.lineWidth = 1.0
        let strokeColor = UIColor.blue
        strokeColor.setStroke()

        // Move the path to a new location
        path.apply(CGAffineTransform(translationX: 10, y: 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
    }
}

das gibt uns das gleiche Ergebnis ...

enter image description here

Weitere Studie

I really recommend looking at the following materials. They are what finally made Bézier paths understandable for me. (And taught me how to pronounce it: /ˈbɛ zi eɪ/.)

223
Suragch

Es wäre einfacher, wenn Sie CAShapeLayer wie folgt verwenden würden:

CAShapeLayer *shapeView = [[CAShapeLayer alloc] init];

Und setze sein path:

[shapeView setPath:[self createPath].CGPath];

Füge es schließlich hinzu:

[[self.view layer] addSublayer:shapeView];
53
Rui Peres

Sie können dazu ein CAShapeLayer verwenden.

So was...

CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [self createPath].CGPath;
shapeLayer.strokeColor = [UIColor redColor].CGColor; //etc...
shapeLayer.lineWidth = 2.0; //etc...
shapeLayer.position = CGPointMake(100, 100); //etc...
[self.layer addSublayer:shapeLayer];

Dadurch wird der Pfad hinzugefügt und gezeichnet, ohne dass drawRect überschrieben werden muss.

15
Fogmeister

Es gibt mehrere Möglichkeiten, um das zu erreichen, was Sie möchten. Die, die ich am häufigsten gesehen habe, sind: override drawRect, zeichne deine Form in eine CAShapeLayer und füge sie dann als Unterebene zu deiner Ansicht hinzu, oder zeichne deinen Pfad in einen anderen Kontext Speichern Sie das als Bild und fügen Sie es dann Ihrer Ansicht hinzu.

All dies sind vernünftige Entscheidungen, und welche die beste ist, hängt von vielen anderen Faktoren ab, z. B. davon, wie oft Sie kontinuierlich Formen hinzufügen, wie oft sie aufgerufen werden usw.

5
Travis

Wie die anderen Plakate bereits betonten, ist die Verwendung einer Formebene ein guter Weg.

Formebenen a bieten wahrscheinlich eine bessere Leistung als das Überschreiben von drawRect.

Wenn Sie Ihren Pfad selbst zeichnen möchten, müssen Sie drawRect für Ihre benutzerdefinierte Ansichtsklasse überschreiben.

2
Duncan C

Ja, Sie müssen das drawrect überschreiben, wenn Sie etwas zeichnen möchten. Das Erstellen eines UIBezierPath kann überall erfolgen, aber um etwas zu zeichnen, müssen Sie es in der drawrect -Methode ausführen

Sie sollten setNeedsDisplay aufrufen, wenn Sie drawRect in einer Unterklasse von UIView überschreiben, bei der es sich im Grunde um eine benutzerdefinierte Ansicht handelt, die etwas auf dem Bildschirm zeichnet, z. B. Linien, Bilder, Rechtecke.

1
Lithu T.V