使用UIBezierPath绘制圆角

7

我有一个设计元素,但是我不知道如何构建它,希望有人能够指点我。我想要构建的元素如下所示:

Rounded Button With No Bottom Border

实际上,它是一个带有左侧、顶部和右侧边框的圆角矩形(底部不应有边框)。

我尝试过使用以下代码:

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

随后,我使用以下代码在矩形周围绘制描边线条;
// 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)

这将导致以下图像;

Rounded Rect with Stroke

我还没有找到如何移除底部的线条或者使用UIBezierPath() 绘制这个元素,但是需要以与圆角矩形相同的方式来圆角化,因为我在同一视图中使用了另一个圆角矩形来完成不同的目的,所以这些圆角需要相同。

谢谢!

2个回答

10

CGMutablePath 的方法 addArc(tangent1End:tangent2End:radius:transform:) 旨在轻松制作圆角。

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

一旦您拥有该方法,最好使用自定义视图来管理CAShapeLayer,以便它可以自动适应大小更改。演示:

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

结果:

演示


我很高兴你加上了这个。在贝塞尔路径没有自动圆角之前,我们以前经常这样做!我之所以没有在这里使用它,是因为我想坚持原始问题中OP开始的确切贝塞尔路径,因为那是问题规定的一部分。 - matt
你无法想象我有多高兴能找到这个。如果有机会,我会给你赏金的。非常感谢。 - ysnzlcn

6
不要使用形状图层,而是使用一个图层(或视图)。将UIBezierPath的路径绘制到其中并描边,然后通过使用“ .clear”混合模式绘制并描边底部线条以擦除它。

enter image description here

代码(根据需要进行修改;我在这里使用一个清晰的UIView作为其draw代码来绘制形状):

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)

编辑 另一种方法是在绘制圆角矩形之前剪切底部线条区域:

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

那个完美地运行了!谢谢,@matt!我过于复杂化了这个问题;我没有意识到我可以使用.clear混合模式进行描边。现在很清楚了! - ZbadhabitZ
还有另一种方法,就是在绘制圆角矩形之前进行裁剪。也许我也应该展示一下这种方法。 - matt
添加了代码来演示第二种方式,即裁剪。实际上,在这种情况下我更喜欢裁剪;它更容易控制。 - matt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接