丸みを帯びた角を作成できましたが、最初の丸みを帯びた角(右下)に問題があります。
質問 :
これがカスタム長方形とスクリーンショットの私のコードです:
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()
コードを直線で始める代わりに:
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
これを行うことで、4つの丸い角ができ、コードの直前に直線を追加する必要があります。
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()
あなたがしていることは非常に複雑だと思います。 UIBezierPathはUIBezierPath(roundedRect:)
を提供するので、それを使用してみませんか?角の丸い長方形をなでます。小さな三角形を配置する場所を消去します。三角形を追加します。複合パスを埋めます。三角形の欠けている2つの辺をストロークします。このように(これは私がたまたま横になっていたコードです-もちろん、あなたの形に合うように数字を変更する必要があります):
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)
いくつかの観察:
ビューbounds
を取得し、線幅の半分だけ挿入してください。これにより、ストロークされた境界線全体がビューのbounds
内に収まります。線幅が1の場合、これはそれほど明白ではないかもしれませんが、線幅が大きいほど、問題はより顕著になります。
draw(_:)
メソッドを使用する場合は、このメソッドに渡されるrect
を使用せず、bounds
(上記の挿入図)を参照してください。 draw(_:)
に渡されるCGRect
は描画される長方形であり、必ずしも完全なbounds
である必要はありません。 (常にではありませんが、通常は、このメソッドに渡されるbounds
ではなく、常にビューのrect
を参照します。)
ドキュメント が言うように(強調が追加されました):
更新が必要なビューの境界の部分。ビューを初めて描画するとき、この長方形は通常、ビューの表示範囲全体です。 ただし、後続の描画操作中に、長方形はビューの一部のみを指定する場合があります。
ビューのさまざまなプロパティすべてに、ビューの再描画をトリガーするdidSet
オブザーバーを指定します。そうすれば、IBオーバーライドまたはプログラムで設定された値は、結果のビューに自動的に反映されます。
必要に応じて、すべてを作成できます@IBDesignable
そしてプロパティを@IBInspectable
なので、これはInterfaceBuilderでレンダリングされます。必須ではありませんが、ストーリーボードまたはNIBでレンダリングされたものを見たい場合に役立ちます。
円弧を使用して角を丸めることはできますが、四角形の曲線を使用する方が簡単です、私見。円弧が終了する場所と長方形のコーナーを指定するだけで、2次ベジェがきれいに丸みを帯びたコーナーを生成します。この手法を使用すると、角度や円弧の中心を計算する必要はありません。
したがって:
@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()
}
}
その結果:
これを自動的に行うことはできません。線を短くしてから、コーナー半径にしたい半径の円弧を使用する必要があります。
そう。 x、yに線を追加する代わりに、x-radius、yに線を追加します。次に、円弧を追加します。次に、次の行はx、y + radiusから始まります。
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()
}