1. ホーム
  2. ios

UIBezierPath をコード生成された UIView 上に描画する。

2023-10-09 21:47:50

質問

私は UIView が実行時にコードで追加されます。

を描画したい。 UIBezierPath を描画したいのですが、この場合は drawRect をオーバーライドする必要があるということでしょうか?

または、カスタムメイドの UIView ?

を生成するコードは次のとおりです。 UIView :

UIView* shapeView = [[UIView alloc]initWithFrame:CGRectMake(xOrigin,yOrigin+(i*MENU_BLOCK_FRAME_HEIGHT), self.shapeScroll.frame.size.width, MENU_BLOCK_FRAME_HEIGHT)];
shapeView.clipsToBounds = YES;

そして、以下は UIBezierPath :

- (UIBezierPath*)createPath
{
    UIBezierPath* path = [[UIBezierPath alloc]init];
    [path moveToPoint:CGPointMake(100.0, 50.0)];
    [path addLineToPoint:CGPointMake(200.0,50.0)];
    [path addLineToPoint:CGPointMake(200.0, 200.0)];
    [path addLineToPoint:CGPointMake(100.0, 200.0)];
    [path closePath];
    return path;
}

どのように解決するのですか?

少し前までは、ベジエの発音はおろか、ベジエ パスを使用してカスタム形状を作成する方法さえ知りませんでした。以下は、私が学んだことです。最初見たときほど怖いものではないことがわかりました。

を描く方法 ベジエパス をカスタムビューで描画する方法

以上が主な手順です。

  1. 必要な形状のアウトラインをデザインします。
  2. アウトラインのパスを直線、円弧、曲線のセグメントに分割する。
  3. プログラム的にそのパスを構築する。
  4. パスの描画は drawRect を使うか、あるいは CAShapeLayer .

デザイン形状の概要

何でもいいのですが、例として下のような形を選びました。キーボードのポップアップキーのようなものです。

パスをセグメントに分割する

図形の設計を振り返って、直線(直線)、円弧(円や丸い角)、曲線(それ以外)の単純な要素に分解します。

私たちのデザイン例では、このようになります。

  • 黒は線分
  • 水色は円弧セグメント
  • 赤色はカーブ
  • オレンジの点は曲線の制御点
  • 緑色の点は、パスセグメント間のポイント
  • 点線は、境界矩形を示す
  • 紺色の数字は、プログラムによって追加される順番のセグメントです。

プログラムによるパスの構築

任意で左下から始めて時計回りに動作させることにします。画像のグリッドを使用して、点の x と y の値を取得します。ここではすべてをハードコードしていますが、もちろん実際のプロジェクトではそんなことはしないでしょう。

基本的な流れは

  1. 新しい UIBezierPath
  2. パス上の開始点を moveToPoint
  3. パスにセグメントを追加する
    • の行を追加します。 addLineToPoint
    • 円弧 addArcWithCenter
    • 曲線 addCurveToPoint
  4. パスを閉じるには closePath

上の画像のようなパスを作るためのコードです。

func createBezierPath() -> UIBezierPath {

    // create a new path
    let path = UIBezierPath()

    // starting point for the path (bottom left)
    path.move(to: CGPoint(x: 2, y: 26))

    // *********************
    // ***** Left side *****
    // *********************

    // segment 1: line
    path.addLine(to: CGPoint(x: 2, y: 15))

    // segment 2: curve
    path.addCurve(to: CGPoint(x: 0, y: 12), // ending point
        controlPoint1: CGPoint(x: 2, y: 14),
        controlPoint2: CGPoint(x: 0, y: 14))

    // segment 3: line
    path.addLine(to: CGPoint(x: 0, y: 2))

    // *********************
    // ****** Top side *****
    // *********************

    // segment 4: arc
    path.addArc(withCenter: CGPoint(x: 2, y: 2), // center point of circle
        radius: 2, // this will make it meet our path line
        startAngle: CGFloat(M_PI), // π radians = 180 degrees = straight left
        endAngle: CGFloat(3*M_PI_2), // 3π/2 radians = 270 degrees = straight up
        clockwise: true) // startAngle to endAngle goes in a clockwise direction

    // segment 5: line
    path.addLine(to: CGPoint(x: 8, y: 0))

    // segment 6: arc
    path.addArc(withCenter: CGPoint(x: 8, y: 2),
                          radius: 2,
                          startAngle: CGFloat(3*M_PI_2), // straight up
        endAngle: CGFloat(0), // 0 radians = straight right
        clockwise: true)

    // *********************
    // ***** Right side ****
    // *********************

    // segment 7: line
    path.addLine(to: CGPoint(x: 10, y: 12))

    // segment 8: curve
    path.addCurve(to: CGPoint(x: 8, y: 15), // ending point
        controlPoint1: CGPoint(x: 10, y: 14),
        controlPoint2: CGPoint(x: 8, y: 14))

    // segment 9: line
    path.addLine(to: CGPoint(x: 8, y: 26))

    // *********************
    // **** Bottom side ****
    // *********************

    // segment 10: line
    path.close() // draws the final line to close the path

    return path
}

注:上記のコードのいくつかは、1つのコマンドで線と弧を追加することで削減できます(弧は暗黙のうちに始点を持つため)。以下参照 を参照してください。 を参照してください。

パスを描く

パスの描画は、レイヤー内または drawRect .

方法1:レイヤーでパスを描画する

カスタムクラスはこのような感じです。ベジェパスを新しい CAShapeLayer に追加しています。

import UIKit
class MyCustomView: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    func setup() {

        // Create a CAShapeLayer
        let shapeLayer = CAShapeLayer()

        // The Bezier path that we made needs to be converted to 
        // a CGPath before it can be used on a layer.
        shapeLayer.path = createBezierPath().cgPath

        // apply other properties related to the path
        shapeLayer.strokeColor = UIColor.blue.cgColor
        shapeLayer.fillColor = UIColor.white.cgColor
        shapeLayer.lineWidth = 1.0
        shapeLayer.position = CGPoint(x: 10, y: 10)

        // add the new layer to our custom view
        self.layer.addSublayer(shapeLayer)
    }

    func createBezierPath() -> UIBezierPath {

        // see previous code for creating the Bezier path
    }
}

そして、ビューコントローラーに以下のようなビューを作成します。

override func viewDidLoad() {
    super.viewDidLoad()

    // create a new UIView and add it to the view controller
    let myView = MyCustomView()
    myView.frame = CGRect(x: 100, y: 100, width: 50, height: 50)
    myView.backgroundColor = UIColor.yellow
    view.addSubview(myView)

}

私たちは...

うーん、これはちょっと小さいですね、すべての数字をハードコードしてしまったからです。でも、このようにパスの大きさを拡大することができますよ。

let path = createBezierPath()
let scale = CGAffineTransform(scaleX: 2, y: 2)
path.apply(scale)
shapeLayer.path = path.cgPath

方法2:パスを draw

使用方法 draw を使用すると、レイヤーに描画するよりも遅くなるので、必要ない場合はこの方法を推奨しません。

以下は、カスタムビューの修正コードです。

import UIKit
class MyCustomView: UIView {

    override func draw(_ rect: CGRect) {

        // create path (see previous code)
        let path = createBezierPath()

        // fill
        let fillColor = UIColor.white
        fillColor.setFill()

        // stroke
        path.lineWidth = 1.0
        let strokeColor = UIColor.blue
        strokeColor.setStroke()

        // Move the path to a new location
        path.apply(CGAffineTransform(translationX: 10, y: 10))

        // fill and stroke the path (always do these last)
        path.fill()
        path.stroke()

    }

    func createBezierPath() -> UIBezierPath {

        // see previous code for creating the Bezier path
    }
}

というようにすると、同じ結果になります...

さらなる研究

I 本当に 以下の資料を見ることをお勧めします。これらの資料によって、私はようやくBézier pathsを理解できるようになりました。(そして /ˈ bɛ zi e↪Ll_26A/ という発音の仕方も教えてくれました)。