【问题标题】:Drawing Rounded Corners Using UIBezierPath使用 UIBezierPath 绘制圆角
【发布时间】:2017-09-12 09:14:23
【问题描述】:

我有一个设计元素我无法弄清楚;希望有人能指出我正确的方向。我正在尝试构建的元素是这样的;

实际上,它是一个圆角矩形,在左侧、顶部和右侧都有一个笔划(底部应该没有笔划)。

我已经尝试过使用以下代码;

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

这会产生以下图像;

我无法弄清楚如何删除底部笔划使用 UIBezierPath() 绘制项目,但以某种方式圆角这将与圆角矩形相同(我在同一视图中使用另一个圆角矩形用于不同的目的,圆角需要相同)。

谢谢!

【问题讨论】:

    标签: ios swift calayer uibezierpath


    【解决方案1】:

    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 开始的确切贝塞尔路径,因为这是问题中规定的一部分。
    • 你无法相信我很高兴找到这个。如果我有机会,会给你赏金。非常感谢。
    【解决方案2】:

    不要使用形状图层。使用图层(或视图)。将 UIBezierPath 的路径绘制到其中并描边,然后通过绘制它并使用.clear 混合模式描边来擦除底线。

    结果:

    代码(根据需要进行修改;我在这里使用了一个清晰的 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()
    

    【讨论】:

    • 效果很好!谢谢,@马特!我太复杂了。我没有意识到我可以使用.clear 混合模式进行描边。现在说得通了!
    • 还有一种方法,就是在绘制圆角矩形之前进行剪辑。也许我也应该证明这一点。
    • 添加了代码来演示第二种方式,剪辑。在这种情况下,我实际上更喜欢剪裁;更容易控制。