ZStackでループをつかって複数の図形を描画する

ZStack などの中で複数の同じ図形を描画したいときに思い浮かぶのが for ループを使う方法です。しかし for ループではコンパイルが通りません。

この場合は ForEach ループを使います。このような画面を表示するアプリを例として解説します。

3〜6行目、データ配列の要素になる構造体の型を定義しています。
83〜108行目、表示したい矩形のサイズと描画線の色を指定してデータを初期化しています。
52行目、ForEach のループで描画コードを回します。ここを for ループにするとコンパイルエラーになります。

ForEach の謎の引数 id: ですが、これはいったいなんでしょうか?引数 id: は、ForEach がどの要素が変更されたかを識別するのに使用されます。この場合は自分自身のビュー .self です。

ためしに52行目を ForEach ( 0 ..< Data.count ) のように id: を省略して書いてみると意味がわかると思います。コンパイルは通りますが、画面には何も描画されません。

import SwiftUI

struct SIZE_AND_COLOR{
    var TheSize: CGSize
    var TheColor: Color
}

struct ContentView: View {

    // 描画したい矩形の個数を指定する.
    let REQ_NUM_DATA = 8
    @State var Data: [SIZE_AND_COLOR] = []

    // 描画する矩形の線の太さ.
    let STROKE_LINE_WIDTH = 4.0

    // 描画する矩形の基準サイズ.
    let UNIT_W = 20.0
    let UNIT_H = 40.0

    var body: some View {

        GeometryReader {

            geom in

            ZStack {

                // 背景を塗りつぶす.
                Rectangle()
                    .foregroundColor( .gray )

                // ZStack のサイズを取得する.
                let geom_w = geom.size.width
                let geom_h = geom.size.height

                // ZStack の中心座標を取得する.
                let center_x = geom_w * 0.5
                let center_y = geom_h * 0.5

                // めやすとなる中心十字線を描画する.
                Path{
                    path in
                    path.move( to: CGPoint( x: center_x, y: 0 ) )
                    path.addLine( to: CGPoint( x: center_x, y: geom_h ))
                    path.move( to: CGPoint( x: 0, y: center_y ) )
                    path.addLine( to: CGPoint( x: geom_w, y: center_y ))
                }
                .stroke( .white, lineWidth: 1.0 )

                // データの個数ぶんループする.
                ForEach ( 0 ..< Data.count, id: \.self ) { n in

                    // 描画する線の色をデータから取り出す.
                    let stroke_color = Data[n].TheColor

                    Path{
                        path in

                        // 描画する矩形の幅と高さをデータから取り出す.
                        let w = Data[n].TheSize.width
                        let h = Data[n].TheSize.height

                        // 描画する矩形の左上隅座標を取得する.
                        let x = center_x - w * 0.5
                        let y = center_y - h * 0.5

                        // addRect に渡す引数を作成する.
                        let rect = CGRect( x: x, y: y, width: w, height: h )

                        // 矩形を描画する.
                        path.addRect( rect )

                    }
                    .stroke( stroke_color, lineWidth: STROKE_LINE_WIDTH )

                }

            }


        }
        .onAppear(){

            // ZStack が出現するときに実行されるコード.

            // いったん配列要素をクリアする.
            Data.removeAll()

            // 配列要素の要求作成個数ぶんループする.
            for n in 0 ..< REQ_NUM_DATA {

                // 矩形のサイズ.
                let w = UNIT_W * Double( n + 1 )
                let h = UNIT_H * Double( n + 1 )
                let the_size = CGSize( width: w, height: h )

                // 矩形の描画線の色.
                let r = 0.0
                let g = Double(n)/Double( REQ_NUM_DATA - 1 )
                let b = 0.0
                let the_color = Color( red: r, green: g, blue: b, opacity: 1.0 )

                // 配列に要素を追加する.
                let element = SIZE_AND_COLOR( TheSize: the_size, TheColor: the_color )
                Data.append( element )

            }

        }

    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}