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;
}
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.
Dies sind die Hauptschritte:
drawRect
oder mit einem CAShapeLayer
.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.
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:
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:
UIBezierPath
moveToPoint
addLineToPoint
addArcWithCenter
addCurveToPoint
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.
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...
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
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 ...
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ɪ/.)
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];
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.
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.
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.
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.