web-dev-qa-db-ja.com

UIBezierPathカスタム長方形に丸みを帯びた角を追加するにはどうすればよいですか?

丸みを帯びた角を作成できましたが、最初の丸みを帯びた角(右下)に問題があります。

質問 :

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

enter image description here

16
AziCode

コードを直線で始める代わりに:

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

enter image description here

10
AziCode

あなたがしていることは非常に複雑だと思います。 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)

enter image description here

19
matt

いくつかの観察:

  1. ビューboundsを取得し、線幅の半分だけ挿入してください。これにより、ストロークされた境界線全体がビューのbounds内に収まります。線幅が1の場合、これはそれほど明白ではないかもしれませんが、線幅が大きいほど、問題はより顕著になります。

  2. draw(_:)メソッドを使用する場合は、このメソッドに渡されるrectを使用せず、bounds(上記の挿入図)を参照してください。 draw(_:)に渡されるCGRectは描画される長方形であり、必ずしも完全なboundsである必要はありません。 (常にではありませんが、通常は、このメソッドに渡されるboundsではなく、常にビューのrectを参照します。)

    ドキュメント が言うように(強調が追加されました):

    更新が必要なビューの境界の部分。ビューを初めて描画するとき、この長方形は通常、ビューの表示範囲全体です。 ただし、後続の描画操作中に、長方形はビューの一部のみを指定する場合があります。

  3. ビューのさまざまなプロパティすべてに、ビューの再描画をトリガーするdidSetオブザーバーを指定します。そうすれば、IBオーバーライドまたはプログラムで設定された値は、結果のビューに自動的に反映されます。

  4. 必要に応じて、すべてを作成できます@IBDesignableそしてプロパティを@IBInspectableなので、これはInterfaceBuilderでレンダリングされます。必須ではありませんが、ストーリーボードまたはNIBでレンダリングされたものを見たい場合に役立ちます。

  5. 円弧を使用して角を丸めることはできますが、四角形の曲線を使用する方が簡単です、私見。円弧が終了する場所と長方形のコーナーを指定するだけで、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()
    }
}

その結果:

enter image description here

2
Rob

これを自動的に行うことはできません。線を短くしてから、コーナー半径にしたい半径の円弧を使用する必要があります。

そう。 x、yに線を追加する代わりに、x-radius、yに線を追加します。次に、円弧を追加します。次に、次の行はx、y + radiusから始まります。

1
Fogmeister

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()
}
0
Andrew Krotov