質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.11%

UIBezierPathを使用した図形の描画について

受付中

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 761

wai21

score 30

 前提・実現したいこと

view 全体にグリッド線を描画したい。

UIView のサブクラス(下記ソースのCanvasView)で、そのビュー全体にグリッド線を描画するために、 UIView のdraw(_ rect: CGRect) をオーバーライドし、UIBezierPath を使用してグリッド線を描画しているのですが、ビューの大きさが大きくなると、グリッドが表示されません。
draw(_ rect: CGRect) は呼ばれているのですが、エラーも出力されておらず、原因がわかりません。
また、init(frame: CGRect) でbackgroundColor の設定も行なっていますが、こちらも反映されなくなります。

下記ソースではボタンでスクロールビューに載せるキャンバスを切り替えていますが、
小さいキャンバンスはグリッド線が描画されますが、
大きいキャンバスはグリッド線が描画されません。

原因および対策方法をご教授ください。

 クラスの説明

ViewController :スクロールビュー上にキャンバスを表示する
CanvasView :グリッドを描画するUIView のサブクラス
Grid :グリッドを描画するために、グリッドの各マス目の四隅のポイントを保持する

 グリッドの描画処理

CanvasView におけるグリッドの描画処理の順番です。

  1. init(frame: CGRect) を呼ぶ
  2. backgroundColor に.yellow を設定する
  3. initGrid() にてビュー全体におさまるだけのGrid の配列を作成しgrids に設定する
  4. draw(_ rect: CGRect) にて、grids のすべての要素について、UIBezeriPathを使用して各ポイントを線でつなぎ、グリッド線とする
class ViewController: UIViewController {
    // キャンバス
    var canvasView: CanvasView!
    // キャンバスを載せるスクロールビュー
    @IBOutlet weak var scrollView: UIScrollView!

    // 小さいキャンバスを表示する
    @IBAction func small(_ sender: UIButton) {
        canvasView.removeFromSuperview()

        canvasView = CanvasView(frame: scrollView.bounds)

        scrollView.addSubview(canvasView)
        scrollView.contentSize = canvasView.frame.size
        scrollView.contentOffset = CGPoint(x: 0, y: 0)
    }
    // 大きいキャンバスを表示する
    @IBAction func big(_ sender: UIButton) {
        canvasView.removeFromSuperview()

        canvasView = CanvasView(frame: CGRect(x: 0, y: 0, width: 6000, height: 4000))

        scrollView.addSubview(canvasView)
        scrollView.contentSize = canvasView.frame.size
        scrollView.contentOffset = CGPoint(x: 0, y: 0)
    }
    override func viewDidLoad() {
        super.viewDidLoad()

    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    override func viewDidLayoutSubviews() {
        // 小さいキャンバスを表示する
        canvasView = CanvasView(frame: scrollView.bounds)
        scrollView.addSubview(canvasView)
        scrollView.contentSize = canvasView.frame.size
    }
}

//  キャンバスのグリッドの一つ一つのマス目
class Grid {
    // 左上
    var upperLeft: CGPoint!
    // 右上
    var upperRight: CGPoint {
        return CGPoint(x: upperLeft.x + width, y: upperLeft.y)
    }
    // 左下
    var lowerLeft: CGPoint {
        return CGPoint(x: upperLeft.x, y: upperLeft.y + height)
    }
    // 右下
    var lowerRight: CGPoint {
        return CGPoint(x: upperLeft.x + width, y: upperLeft.y + height)
    }

    init(upperLeft: CGPoint) {
        self.upperLeft = upperLeft
    }
    private let width: CGFloat = 30
    private let height: CGFloat = 20
}

// キャンバス
class CanvasView: UIView {
    // グリッドの一つ一つのマス目を配列で保持する
    var grids = Array<Grid>()

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .yellow
        initGrid()
    }
    // 画面いっぱいのグリッドのマス目を作成する
    func initGrid() {
        var point = CGPoint(x: 20, y: 20)
        while point.y < frame.height {
            while point.x < frame.width {
                grids.append(Grid(upperLeft: point))
                point.x = point.x + 30
            }
            point.x = 20
            point.y = point.y + 20
        }
    }
    // gridsの各要素について、グリッドを描画する
    override func draw(_ rect: CGRect) {
        let path = UIBezierPath(rect: bounds)
        path.lineWidth = 0.5
        UIColor.gray.set()

        for grid in grids {
            path.move(to: grid.upperLeft)
            path.addLine(to: grid.upperRight)
            path.addLine(to: grid.lowerRight)
            path.addLine(to: grid.lowerLeft)
            path.close()
        }
        path.stroke()
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

 補足情報(FW/ツールのバージョンなど)

環境
Xcode Version 9.2 (9C40b)
Swift Version 4.0.3

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

0

gridsは初期状態でのUIViewの大きさに合わせて作られていますので、UIViewの大きさが変わった時に作り直す必要がありますね

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/19 16:46 編集

    小さいキャンバスと大きいキャンバスの入れ替えはViewController のsmall(_ sender: UIButton) と big(_ sender: UIButton) でそれぞれ行なっているのですが、CanvasView を新しく作り直しており、grids も作り直されていることを確認しました。
    しかし、やはり大きいキャンバスのみグリッド線が描画されません。

    UIBezierPath もしくはdraw(_ rect: CGRect) に何か制限があるのでしょうか。
    描画範囲の大きさや処理時間等。。。

    キャンセル

  • 2018/03/19 17:19

    見落としてました。

    そのほかの部分ですとUIBezierPathはrectを指定せずに単にUIBezierPath()とした方がよさそうです。
    が、アフィン変換などが掛かってなければboundsとframeの大きさは同じなので関係はなさそうです。

    キャンセル

  • 2018/03/19 17:39

    確かに、rectを指定してもしなくても同様の結果でした。
    スクロールビューのインディケーターは表示されるので、指定のサイズのビューは作成されているようでした。
    ご回答ありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.11%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る