2017-04-16 6 views
1

Ich habe ein Design-Element, das ich habe Probleme herauszufinden; Ich hoffe, dass jemand mich in die richtige Richtung weisen kann. Das Element, das ich zu bauen versuche, ist so;Zeichnen abgerundete Ecken mit UIBezierPath

Rounded Button With No Bottom Border

effektiv, es ist ein Rechteck mit abgerundeten Ecken mit einem Schlag auf der linken, oberen und rechten Seite (der Boden soll keinen Schlaganfall).

Ich habe versucht, den folgenden Code zu verwenden;

// Create the rounded rectangle 
let maskPath = UIBezierPath(roundedRect: myView.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: 4.0, height: 4.0)) 

// Setup a shape layer 
let shape = CAShapeLayer() 

// Create the shape path 
shape.path = maskPath.cgPath 

// Apply the mask 
myView.layer.mask = shape 

Anschließend verwende ich Folgendes, um den Strich um das Rect zu zeichnen;

// Add border 
let borderLayer = CAShapeLayer() 
borderLayer.path = maskPath.cgPath 
borderLayer.fillColor = UIColor.clear.cgColor 
borderLayer.strokeColor = UIColor.white.cgColor 
borderLayer.lineWidth = 2.0 
borderLayer.frame = self.bounds 
self.layer.addSublayer(borderLayer) 

Dies ergibt das folgende Bild;

Rounded Rect with Stroke

Ich habe nicht in der Lage, herauszufinden, wie entweder den unteren Hub entfernen oder das Element zeichnen ein UIBezierPath(), wobei aber die Ecken in einer Weise gerundet, die identisch sind zu der abgerundeten rect (ich benutze eine andere abgerundete rect in der gleichen Ansicht für einen anderen Zweck, und die abgerundeten Ecken müssten identisch sein).

Danke!

Antwort

2

Verwenden Sie keine Formebene. Verwenden Sie eine Ebene (oder eine Ansicht). Zeichnen Sie den Pfad des UIBezierPfad ein und streichen Sie ihn. Löschen Sie dann die untere Linie, indem Sie sie zeichnen und mit einem .clear Mischmodus streichen.

Ergebnis:

enter image description here

-Code (nach Wunsch ändern, ich hier eine klare UIView verwenden, die die Form als draw Code zeichnet):

let p = UIBezierPath(roundedRect: self.bounds, 
     byRoundingCorners: [.topLeft, .topRight], 
     cornerRadii: CGSize(width: 4.0, height: 4.0)) 
    UIColor.white.setStroke() 
    p.stroke() 
    let p2 = UIBezierPath() 
    p2.move(to: CGPoint(x:0, y:self.bounds.height)) 
    p2.addLine(to: CGPoint(x:self.bounds.width, y:self.bounds.height)) 
    p2.lineWidth = 2 
    p2.stroke(with: .clear, alpha: 1) 

EDIT Eine andere Möglichkeit wäre wurden, um den unteren Linienbereich vor dem Zeichnen der abgerundeten Rect auszuschneiden:

let p1 = UIBezierPath(rect: CGRect(origin:.zero, 
     size:CGSize(width:self.bounds.width, height:self.bounds.height-2))) 
    p1.addClip() 
    let p = UIBezierPath(roundedRect: self.bounds, 
     byRoundingCorners: [.topLeft, .topRight], 
     cornerRadii: CGSize(width: 4.0, height: 4.0)) 
    UIColor.white.setStroke() 
    p.stroke() 
+0

Das funktionierte perfekt! Danke, @matt! Ich habe diesen Weg zu sehr überkompliziert; Ich wusste nicht, dass ich mit einem '.clear' Blend-Modus streichen konnte. Macht jetzt Sinn! – ZbadhabitZ

+0

Es gibt eine andere Möglichkeit, vor dem Zeichnen der abgerundeten Rect zu klammern. Vielleicht hätte ich das auch zeigen sollen. – matt

+0

Code hinzugefügt, um den zweiten Weg, Clipping, zu demonstrieren.Ich mag es tatsächlich, in diesem Fall besser zu schneiden; es ist einfacher zu kontrollieren. – matt

1

Die CGMutablePath Methode addArc(tangent1End:tangent2End:radius:transform:) wurde entwickelt, um runde Ecken leicht zu machen.

extension CGMutablePath { 
    static func bottomlessRoundedRect(in rect: CGRect, radius: CGFloat) -> CGMutablePath { 
     let path = CGMutablePath() 
     path.move(to: CGPoint(x: rect.minX, y: rect.maxY)) 
     path.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.minY), tangent2End: CGPoint(x: rect.maxX, y: rect.minY), radius: radius) 
     path.addArc(tangent1End: CGPoint(x: rect.maxX, y: rect.minY), tangent2End: CGPoint(x: rect.maxX, y: rect.maxY), radius: radius) 
     path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY)) 
     return path 
    } 
} 

Sobald Sie diese Methode hat, ist es am besten, eine benutzerdefinierte Ansicht zu verwenden, die CAShapeLayer so verwalten es automatisch auf Größenänderungen anpassen kann. Demo:

class MyFrameView: UIView { 
    override class var layerClass: AnyClass { return CAShapeLayer.self } 

    override func layoutSubviews() { 
     super.layoutSubviews() 
     let layer = self.layer as! CAShapeLayer 
     layer.lineWidth = 2 
     layer.strokeColor = UIColor.white.cgColor 
     layer.fillColor = nil 
     layer.path = CGMutablePath.bottomlessRoundedRect(in: bounds.insetBy(dx: 10, dy: 10), radius: 8) 
    } 
} 

import PlaygroundSupport 

let view = UIView(frame: CGRect(x: 0, y: 0, width: 120, height: 60)) 
view.backgroundColor = #colorLiteral(red: 0.7034167647, green: 0.4845994711, blue: 0.6114708185, alpha: 1) 
let frameView = MyFrameView(frame: view.bounds) 
frameView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 
view.addSubview(frameView) 
let label = UILabel(frame: view.bounds) 
label.text = "Hello" 
label.textColor = .white 
label.textAlignment = .center 
view.addSubview(label) 

PlaygroundPage.current.liveView = view 

Ergebnis:

demo

+0

Ich bin froh, dass Sie das hinzugefügt haben. Wir haben das immer wieder gemacht, bevor Bezierwege automatisch abgerundet wurden! Der einzige Grund, warum ich es hier nicht verwendet habe, war, dass ich bei dem genauen Bezier-Pfad bleiben wollte, mit dem das OP begonnen hatte, denn das war Teil der Bestimmung in der Frage. – matt