Drawing Rounded Corners Using UIBezierPath

帅比萌擦擦* 提交于 2020-02-24 06:31:08

问题


I have a design element that I'm having trouble figuring out; hoping someone may be able to point me in the right direction. The element I am trying to build is like so;

Effectively, it's a rounded rectangle with a stroke on the left, top, and right sides (the bottom should have no stroke).

I've dabbled in using the following code;

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

Subsequently, I'm using the following to draw the stroke around the rect;

// 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)

This results in the following image;

I've not been able to figure out how to either remove the bottom stroke or draw the item using a UIBezierPath(), but rounding the corners in a way that would be identical to the rounded rect (I'm using another rounded rect in the same view for a different purpose, and the rounded corners would need to be identical).

Thanks!


回答1:


Don't use a shape layer. Use a layer (or a view). Draw the UIBezierPath's path into it and stroke it, and then erase the bottom line by drawing it and stroking it with a .clear blend mode.

Result:

Code (modify as desired; I use here a clear UIView that draws the shape as its draw code):

    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 Another way would have been to clip out the bottom line area before drawing the rounded rect:

    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()



回答2:


The CGMutablePath method addArc(tangent1End:tangent2End:radius:transform:) is designed to make round corners easily.

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

Once you have that method, it's best to use a custom view to manage the CAShapeLayer so it can adapt to size changes automatically. 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

Result:



来源:https://stackoverflow.com/questions/43439854/drawing-rounded-corners-using-uibezierpath

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!