如何将圆角添加到 UIBezierPath 自定义矩形?
Posted
技术标签:
【中文标题】如何将圆角添加到 UIBezierPath 自定义矩形?【英文标题】:How to add rounded corner to a UIBezierPath custom rectangle? 【发布时间】:2015-07-03 21:57:15 【问题描述】:我设法创建了圆角,但我在第一个圆角(右下)遇到问题
问题:
我可以在 (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()
【问题讨论】:
那么剩下的呢?你实际上得到了类似的效果。 【参考方案1】:我认为您正在做的事情过于复杂。 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)
【讨论】:
如果您打算制作“strokeStart”和“strokeEnd”动画,那么不使用它的一个很好的理由是。在这种情况下,矩形开始画线的“位置”很重要。使用路径时,您可以控制它。【参考方案2】:几个观察:
确保您使用视图bounds
并将其插入线宽的一半。这确保了整个描边边框落在视图的bounds
内。如果你的线宽为 1,这可能不那么明显,但线宽越大,问题就越明显。
如果使用draw(_:)
方法,请不要使用传递给此方法的rect
,而是参考bounds
(插入,如上所述)。传递给draw(_:)
的CGRect
是正在绘制的矩形,不一定是完整的bounds
。 (通常是,但并非总是如此,所以总是引用视图的bounds
,而不是传递给此方法的rect
。)
正如the documentation 所说(强调):
需要更新的视图边界部分。第一次绘制视图时,这个矩形通常是视图的整个可见边界。 但是,在后续的绘图操作中,矩形可能只指定了视图的一部分。
我会给视图的所有各种属性一个didSet
观察者,它将触发视图被重绘。这样,任何 IB 覆盖或以编程方式设置的值都将自动反映在结果视图中。
虽然您可以使用圆弧圆角,但使用四边形曲线更容易,恕我直言。您只需指定圆弧的结束位置和矩形的角,二次贝塞尔曲线将产生一个很好的圆角。使用这种技术,不需要计算角度或圆弧的中心。
因此:
@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()
产生:
【讨论】:
哇!这真的是很好的答案!我试图找出解决问题的方法,用 UIBezierPath 设置角落,我自己得到了我想要的。但如果我能找到这个答案,我想我可以更快地得到答案!很好!!【参考方案3】:而不是用直线开始代码:
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()
【讨论】:
很高兴你找到了它。这实际上是我所描述的。 :D 为了改善这一点,您应该将半径、高度和宽度放入变量中。这样,您只需更改一个值即可使气泡具有任意大小和任意圆角半径。 我应该把 x 和 y 放在变量中吗?但是它们从一行代码到另一行是不同的。你能解释更多吗?谢谢 啊。好吧,我实际上会在可以独立定位的单独 UIView 上绘制它。然后气泡的位置将从 (0, 0) 开始。在这种情况下,尽管您的 x y 值是气泡位置及其大小的函数。右下角是 (x + width, y + height) 等等。【参考方案4】: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()
【讨论】:
感谢您的回答!您可以简单地使用 CGFloat.pi 而不是 CGFloat(Double.pi)【参考方案5】:您无法自动执行此操作。您必须使线条更短,然后使用您想要的圆角半径的圆弧。
所以。不是向 x,y 添加一条线,而是将线添加到 x-radius, y。 然后添加圆弧。然后下一行从 x, y+radius 开始。
【讨论】:
@AziCode 我明天可以。不过现在是凌晨 1 点 30 分。 :-)以上是关于如何将圆角添加到 UIBezierPath 自定义矩形?的主要内容,如果未能解决你的问题,请参考以下文章
像在绘图应用程序中一样指定具有角半径的 UIBezierPath 点(例如 Sketch)