Ios 如何向UIBezierPath自定义矩形添加圆角?

Ios 如何向UIBezierPath自定义矩形添加圆角?,ios,swift,uibezierpath,Ios,Swift,Uibezierpath,我设法创建了圆角,但第一个圆角(右下角)有问题 问题: 我可以在(moveToPoint)方法之前添加(addArcWithCenter)方法吗 如何去掉矩形(右下角)开头的直线 以下是自定义矩形的代码和屏幕截图: let path = UIBezierPath() path.moveToPoint(CGPoint(x: 300, y: 0)) path.addArcWithCenter(CGPoint(x: 300-10, y: 50), radius: 10 , startAngle:

我设法创建了圆角,但第一个圆角(右下角)有问题

问题:

  • 我可以在(moveToPoint)方法之前添加(addArcWithCenter)方法吗
  • 如何去掉矩形(右下角)开头的直线
以下是自定义矩形的代码和屏幕截图:

let path = UIBezierPath()
path.moveToPoint(CGPoint(x: 300, y: 0))
path.addArcWithCenter(CGPoint(x: 300-10, y: 50), radius: 10 , startAngle: 0 , endAngle: CGFloat(M_PI/2)  , clockwise: true) //1st rounded corner
path.addArcWithCenter(CGPoint(x: 200, y: 50), radius:10, startAngle: CGFloat(2 * M_PI / 3), endAngle:CGFloat(M_PI) , clockwise: true)// 2rd rounded corner
path.addArcWithCenter(CGPoint(x: 200, y: 10), radius:10, startAngle: CGFloat(M_PI), endAngle:CGFloat(3 * M_PI / 2), clockwise: true)// 3rd rounded corner
// little triangle at the bottom
path.addLineToPoint(CGPoint(x:240 , y:0))
path.addLineToPoint(CGPoint(x: 245, y: -10))
path.addLineToPoint(CGPoint(x:250, y: 0))
path.addArcWithCenter(CGPoint(x: 290, y: 10), radius: 10, startAngle: CGFloat(3 * M_PI / 2), endAngle: CGFloat(2 * M_PI ), clockwise: true)
path.closePath()

您不能自动执行此操作。您必须使直线变短,然后使用角半径为所需半径的圆弧

所以。不是将直线添加到x,而是将直线添加到x半径y。
然后添加弧。然后下一行从x,y+半径开始。

我认为你所做的太复杂了。UIBezierPath为您提供了
UIBezierPath(roundedRect:)
那么为什么不使用它呢?画出圆角矩形;擦掉你要放小三角形的地方;添加三角形;填充复合路径;并划过三角形缺少的两条边。就像这样(这只是我碰巧发现的一些代码-当然,你应该更改数字以适合你的形状):


不要以直线开始代码:

path.moveToPoint(CGPoint(x: 300, y: 0))
我改为以圆弧(右上角)开始:

通过这样做,我有四个圆角,我只需要在代码的末尾添加一条直线,然后:

path.closePath()  
以下是代码和屏幕截图:

let path = UIBezierPath()
path.addArcWithCenter(CGPoint(x: 300-10, y: 50), radius: 10 , startAngle: 0 , endAngle: CGFloat(M_PI/2)  , clockwise: true) //1st rounded corner
path.addArcWithCenter(CGPoint(x: 200, y: 50), radius:10, startAngle: CGFloat(2 * M_PI / 3), endAngle:CGFloat(M_PI) , clockwise: true)// 2rd rounded corner
path.addArcWithCenter(CGPoint(x: 200, y: 10), radius:10, startAngle: CGFloat(M_PI), endAngle:CGFloat(3 * M_PI / 2), clockwise: true)// 3rd rounded corner
// little triangle
path.addLineToPoint(CGPoint(x:240 , y:0))
path.addLineToPoint(CGPoint(x: 245, y: -10))
path.addLineToPoint(CGPoint(x:250, y: 0))
path.addArcWithCenter(CGPoint(x: 290, y: 10), radius: 10, startAngle: CGFloat(3 * M_PI / 2), endAngle: CGFloat(2 * M_PI ), clockwise: true)
path.addLineToPoint(CGPoint(x:300 , y:50))
path.closePath()

带有配置变量的Swift 5

override func draw(_ rect: CGRect) {
    let arrowXOffset: CGFloat = 13
    let cornerRadius: CGFloat = 6
    let arrowHeight: CGFloat = 6

    let mainRect = CGRect(origin: rect.origin, size: CGSize(width: rect.width, height: rect.height - arrowHeight))

    let leftTopPoint = mainRect.origin
    let rightTopPoint = CGPoint(x: mainRect.maxX, y: mainRect.minY)
    let rightBottomPoint = CGPoint(x: mainRect.maxX, y: mainRect.maxY)
    let leftBottomPoint = CGPoint(x: mainRect.minX, y: mainRect.maxY)

    let leftArrowPoint = CGPoint(x: leftBottomPoint.x + arrowXOffset, y: leftBottomPoint.y)
    let centerArrowPoint = CGPoint(x: leftArrowPoint.x + arrowHeight, y: leftArrowPoint.y + arrowHeight)
    let rightArrowPoint = CGPoint(x: leftArrowPoint.x + 2 * arrowHeight, y: leftArrowPoint.y)

    let path = UIBezierPath()
    path.addArc(withCenter: CGPoint(x: rightTopPoint.x - cornerRadius, y: rightTopPoint.y + cornerRadius), radius: cornerRadius,
                startAngle: CGFloat(3 * Double.pi / 2), endAngle: CGFloat(2 * Double.pi), clockwise: true)
    path.addArc(withCenter: CGPoint(x: rightBottomPoint.x - cornerRadius, y: rightBottomPoint.y - cornerRadius), radius: cornerRadius,
                startAngle: 0, endAngle: CGFloat(Double.pi / 2), clockwise: true)

    path.addLine(to: rightArrowPoint)
    path.addLine(to: centerArrowPoint)
    path.addLine(to: leftArrowPoint)

    path.addArc(withCenter: CGPoint(x: leftBottomPoint.x + cornerRadius, y: leftBottomPoint.y - cornerRadius), radius: cornerRadius,
                startAngle: CGFloat(Double.pi / 2), endAngle: CGFloat(Double.pi), clockwise: true)
    path.addArc(withCenter: CGPoint(x: leftTopPoint.x + cornerRadius, y: leftTopPoint.y + cornerRadius), radius: cornerRadius,
                startAngle: CGFloat(Double.pi), endAngle: CGFloat(3 * Double.pi / 2), clockwise: true)


    path.addLine(to: rightTopPoint)
    path.close()
}

以下是几点观察:

  • 确保使用视图
    bounds
    并按一半线宽插入。这样可以确保整个笔划边框位于视图的
    边界内。如果线宽为1,这可能不太明显,但如果线宽较大,问题会更加明显

  • 如果使用
    draw(:)
    方法,不要使用传递给此方法的
    rect
    ,而是参考
    边界(插图,如上所述)。传递给
    draw(:)
    CGRect
    是正在绘制的矩形,不一定是完整的
    边界。(通常是这样,但并不总是这样,因此总是参考视图的
    边界
    ,而不是传递给此方法的
    rect
    。)

    如前所述(重点加上):

    视图边界中需要更新的部分。第一次绘制视图时,此矩形通常是视图的整个可见边界。但是,在后续的绘图操作中,矩形可能只指定视图的一部分

  • 我会给视图的所有不同属性一个
    didSet
    观察者,它将触发视图被重新绘制。这样,任何IB覆盖或以编程方式设置的值都将自动反映在结果视图中

  • 如果需要,您可以将整个内容设置为可设计的,并将属性设置为可检测的,这样您就可以在Interface Builder中看到它。这不是必需的,但如果您想在故事板或笔尖中看到渲染效果,它可能很有用

  • IMHO说,虽然可以使用圆弧来圆角,但使用四元曲线更容易。您只需指定圆弧的端点和矩形的角点,二次贝塞尔曲线将生成一个很好的圆角。使用此技术,无需计算角度或圆弧中心

  • 因此:

    这将产生:


    @AziCode我明天可以。但现在是凌晨1:30。:-)那么剩下的是什么呢?你实际上得到了类似的效果。很高兴你找到了。这实际上就是我所描述的要改善这一点,您应该将半径、高度和宽度引入变量中。这样你就可以通过改变一个值使气泡任意大小,任意角半径。我应该把x和y放在变量中吗?但是它们在不同的代码行中是不同的。你能再解释一下吗?谢谢。实际上,我会在一个单独的UIView上绘制它,它可以独立定位。然后气泡的位置将从(0,0)开始。在这种情况下,尽管x y值是气泡位置和大小的函数。右下角是(x+宽度,y+高度)等等。如果您计划制作“strokeStart”和“strokeEnd”动画,那么不使用它的一个很好的理由就是。在这种情况下,rect开始画线的“位置”很重要。使用路径时,您可以控制它。哇!这真是个好答案!我一直在想办法解决我的问题,用UIBezierPath解决问题,我自己得到了我想要的。但是如果我能找到这个答案,我想我能更快地得到答案!很好!!这应该是一个公认的答案。谢谢你的回答!您可以简单地使用CGFloat.pi代替CGFloat(Double.pi)
    let path = UIBezierPath()
    path.addArcWithCenter(CGPoint(x: 300-10, y: 50), radius: 10 , startAngle: 0 , endAngle: CGFloat(M_PI/2)  , clockwise: true) //1st rounded corner
    path.addArcWithCenter(CGPoint(x: 200, y: 50), radius:10, startAngle: CGFloat(2 * M_PI / 3), endAngle:CGFloat(M_PI) , clockwise: true)// 2rd rounded corner
    path.addArcWithCenter(CGPoint(x: 200, y: 10), radius:10, startAngle: CGFloat(M_PI), endAngle:CGFloat(3 * M_PI / 2), clockwise: true)// 3rd rounded corner
    // little triangle
    path.addLineToPoint(CGPoint(x:240 , y:0))
    path.addLineToPoint(CGPoint(x: 245, y: -10))
    path.addLineToPoint(CGPoint(x:250, y: 0))
    path.addArcWithCenter(CGPoint(x: 290, y: 10), radius: 10, startAngle: CGFloat(3 * M_PI / 2), endAngle: CGFloat(2 * M_PI ), clockwise: true)
    path.addLineToPoint(CGPoint(x:300 , y:50))
    path.closePath()
    
    override func draw(_ rect: CGRect) {
        let arrowXOffset: CGFloat = 13
        let cornerRadius: CGFloat = 6
        let arrowHeight: CGFloat = 6
    
        let mainRect = CGRect(origin: rect.origin, size: CGSize(width: rect.width, height: rect.height - arrowHeight))
    
        let leftTopPoint = mainRect.origin
        let rightTopPoint = CGPoint(x: mainRect.maxX, y: mainRect.minY)
        let rightBottomPoint = CGPoint(x: mainRect.maxX, y: mainRect.maxY)
        let leftBottomPoint = CGPoint(x: mainRect.minX, y: mainRect.maxY)
    
        let leftArrowPoint = CGPoint(x: leftBottomPoint.x + arrowXOffset, y: leftBottomPoint.y)
        let centerArrowPoint = CGPoint(x: leftArrowPoint.x + arrowHeight, y: leftArrowPoint.y + arrowHeight)
        let rightArrowPoint = CGPoint(x: leftArrowPoint.x + 2 * arrowHeight, y: leftArrowPoint.y)
    
        let path = UIBezierPath()
        path.addArc(withCenter: CGPoint(x: rightTopPoint.x - cornerRadius, y: rightTopPoint.y + cornerRadius), radius: cornerRadius,
                    startAngle: CGFloat(3 * Double.pi / 2), endAngle: CGFloat(2 * Double.pi), clockwise: true)
        path.addArc(withCenter: CGPoint(x: rightBottomPoint.x - cornerRadius, y: rightBottomPoint.y - cornerRadius), radius: cornerRadius,
                    startAngle: 0, endAngle: CGFloat(Double.pi / 2), clockwise: true)
    
        path.addLine(to: rightArrowPoint)
        path.addLine(to: centerArrowPoint)
        path.addLine(to: leftArrowPoint)
    
        path.addArc(withCenter: CGPoint(x: leftBottomPoint.x + cornerRadius, y: leftBottomPoint.y - cornerRadius), radius: cornerRadius,
                    startAngle: CGFloat(Double.pi / 2), endAngle: CGFloat(Double.pi), clockwise: true)
        path.addArc(withCenter: CGPoint(x: leftTopPoint.x + cornerRadius, y: leftTopPoint.y + cornerRadius), radius: cornerRadius,
                    startAngle: CGFloat(Double.pi), endAngle: CGFloat(3 * Double.pi / 2), clockwise: true)
    
    
        path.addLine(to: rightTopPoint)
        path.close()
    }
    
    @IBDesignable
    class BubbleView: UIView {
        @IBInspectable var lineWidth:    CGFloat = 1       { didSet { setNeedsDisplay() } }
        @IBInspectable var cornerRadius: CGFloat = 10      { didSet { setNeedsDisplay() } }
        @IBInspectable var calloutSize:  CGFloat = 5       { didSet { setNeedsDisplay() } }
        @IBInspectable var fillColor:    UIColor = .yellow { didSet { setNeedsDisplay() } }
        @IBInspectable var strokeColor:  UIColor = .black  { didSet { setNeedsDisplay() } }
    
        override func draw(_ rect: CGRect) {
            let rect = bounds.insetBy(dx: lineWidth / 2, dy: lineWidth / 2)
            let path = UIBezierPath()
    
            // lower left corner
            path.move(to: CGPoint(x: rect.minX + cornerRadius, y: rect.maxY - calloutSize))
            path.addQuadCurve(to: CGPoint(x: rect.minX, y: rect.maxY - calloutSize - cornerRadius),
                              controlPoint: CGPoint(x: rect.minX, y: rect.maxY - calloutSize))
    
            // left
            path.addLine(to: CGPoint(x: rect.minX, y: rect.minY + cornerRadius))
    
            // upper left corner
            path.addQuadCurve(to: CGPoint(x: rect.minX + cornerRadius, y: rect.minY),
                              controlPoint: CGPoint(x: rect.minX, y: rect.minY))
    
            // top
            path.addLine(to: CGPoint(x: rect.maxX - cornerRadius, y: rect.minY))
    
            // upper right corner
            path.addQuadCurve(to: CGPoint(x: rect.maxX, y: rect.minY + cornerRadius),
                              controlPoint: CGPoint(x: rect.maxX, y: rect.minY))
    
            // right
            path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - calloutSize - cornerRadius))
    
            // lower right corner
            path.addQuadCurve(to: CGPoint(x: rect.maxX - cornerRadius, y: rect.maxY - calloutSize),
                              controlPoint: CGPoint(x: rect.maxX, y: rect.maxY - calloutSize))
    
            // bottom (including callout)
            path.addLine(to: CGPoint(x: rect.midX + calloutSize, y: rect.maxY - calloutSize))
            path.addLine(to: CGPoint(x: rect.midX, y: rect.maxY))
            path.addLine(to: CGPoint(x: rect.midX - calloutSize, y: rect.maxY - calloutSize))
            path.close()
    
            fillColor.setFill()
            path.fill()
    
            strokeColor.setStroke()
            path.lineWidth = lineWidth
            path.stroke()
        }
    }