swift 如何在UIBezierPath自定义矩形中添加圆角?

bweufnob  于 2022-10-31  发布在  Swift
关注(0)|答案(5)|浏览(214)

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

  • 是否可以在(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()

f45qwnt8

f45qwnt81#

我觉得你做的事情太复杂了。UIBezierPath提供了UIBezierPath(roundedRect:),为什么不使用它呢?擦去你要放小三角形的地方添加三角形;填充复合路径;然后画出三角形缺少的两边。就像这样(这只是我碰巧手边的一些代码--当然,你应该改变数字来适应你的形状):

let con = UIGraphicsGetCurrentContext()
CGContextTranslateCTM(con, 10, 10)
UIColor.blueColor().setStroke()
UIColor.blueColor().colorWithAlphaComponent(0.4).setFill()
let p = UIBezierPath(roundedRect: CGRectMake(0,0,250,180), cornerRadius: 10)
p.stroke()
CGContextClearRect(con, CGRectMake(20,170,10,11))
let pts = [
    CGPointMake(20,180), CGPointMake(20,200),
    CGPointMake(20,200), CGPointMake(30,180)
]
p.moveToPoint(pts[0])
p.addLineToPoint(pts[1])
p.addLineToPoint(pts[3])
p.fill()
CGContextStrokeLineSegments(con, pts, 4)

zqry0prt

zqry0prt2#

几个观察结果:
1.请确保将视图bounds插入一半的线宽。这样可以确保整个描边边框福尔斯在视图的bounds范围内。如果线宽为1,这可能不太明显,但线宽越大,问题就越明显。
1.如果使用draw(_:)方法,则不要使用传递给此方法的rect,而应引用bounds传递给draw(_:)CGRect是正在绘制的矩形,不一定是完整的bounds。(通常是这样,但并非总是这样,因此始终引用视图的bounds,而不是传递给此方法的rect。)
the documentation所示(着重号为后加):
视图边界中需要更新的部分。第一次绘制视图时,此矩形通常是视图的整个可见边界。* 但是,在后续的绘制操作中,此矩形可能只指定视图的一部分。*
1.我会给予视图的所有不同属性一个didSet观察器,它将触发视图的重绘。这样,任何IB覆盖或编程设置的值都将自动反映在结果视图中。
1.如果你愿意,你可以将整个内容设置为@IBDesignable,并将属性设置为@IBInspectable,这样你就可以在Interface Builder中看到它的呈现。这不是必须的,但是如果你想在故事板或NIB中看到它的呈现,这是很有用的。
1.虽然你可以用圆弧来圆角,但使用四次曲线更简单,IMHO。你只要指定圆弧的终点和矩形的角,二次贝塞尔曲线就会产生一个很好的圆角。使用这种技术,不需要计算Angular 或圆弧的中心。
因此:

@IBDesignable
public class BubbleView: UIView {
    @IBInspectable public var lineWidth:      CGFloat = 1                           { didSet { setNeedsDisplay() } }
    @IBInspectable public var cornerRadius:   CGFloat = 10                          { didSet { setNeedsDisplay() } }
    @IBInspectable public var calloutSize:    CGSize = CGSize(width: 10, height: 5) { didSet { setNeedsDisplay() } }
    @IBInspectable public var fillColor:      UIColor = .yellow                     { didSet { setNeedsDisplay() } }
    @IBInspectable public var strokeColor:    UIColor = .black                      { didSet { setNeedsDisplay() } }

    override public 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.height))
        path.addQuadCurve(to: CGPoint(x: rect.minX, y: rect.maxY - calloutSize.height - cornerRadius),
                          controlPoint: CGPoint(x: rect.minX, y: rect.maxY - calloutSize.height))

        // 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.height - cornerRadius))

        // lower right corner
        path.addQuadCurve(to: CGPoint(x: rect.maxX - cornerRadius, y: rect.maxY - calloutSize.height),
                          controlPoint: CGPoint(x: rect.maxX, y: rect.maxY - calloutSize.height))

        // bottom (including callout)
        path.addLine(to: CGPoint(x: rect.midX + calloutSize.width / 2, y: rect.maxY - calloutSize.height))
        path.addLine(to: CGPoint(x: rect.midX, y: rect.maxY))
        path.addLine(to: CGPoint(x: rect.midX - calloutSize.width / 2, y: rect.maxY - calloutSize.height))
        path.close()

        fillColor.setFill()
        path.fill()

        strokeColor.setStroke()
        path.lineWidth = lineWidth
        path.stroke()
    }
}

得出:

uidvcgyl

uidvcgyl3#

代码不是以直线开头:

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

eqqqjvef

eqqqjvef4#

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

xbp102n05#

你不能自动完成这一步,你必须使线条变短,然后使用你想要的圆角半径的圆弧。
所以,不是在x,y上加一条线,而是在x-半径,y上加一条线,然后加一条弧,下一条线从x,y+半径开始。

相关问题