(2020/8/22 更新)
図形の輪郭を描画する.stroke()の使い方を解説します。
環境
この記事の情報は次のバージョンで動作確認しています。
【Swift】5.2.4
【iOS】13.6
【macOS】Catalina バージョン 10.15.6
基本的な使い方
.stroke()はShapeプロトコルに準じた図形やPathビューの輪郭を描画します。
1 2 3 4 5 6 7 8 |
.stroke(lineWidth: 太さ) .fill(線の色) または .stroke(線の色, lineWidth: 太さ) |
引数(lineWidth)で線の太さを指定します。
デフォルト値として1が設定されている為、省略可能です。
線の色には、Colorやグラデーションの他に画像も指定可能です。
使用例
1 2 3 4 5 6 7 8 9 |
struct ContentView: View { var body: some View { Circle() // 円形のShape .stroke(Color.blue, lineWidth: 20) // パスを描画 .frame(width:200, height: 200) // フレームサイズ指定 } } |
線のスタイルを指定する
次のイニシャライザを使って線のスタイルを指定できます。
1 2 3 4 5 6 7 8 |
.stroke(style: StrokeStyle構造体) .fill(線の色) または .stroke(線の色, style: StrokeStyle構造体) |
線のスタイルはStrokeStyle構造体で定義します。
StrokeStyle構造体
StrokeStyle構造体のイニシャライザは次の通りです。
全ての引数はデフォルト値が設定されているので、省略可能。
必要な引数のみ指定します。
1 2 3 |
StrokeStyle(lineWidth: CGFloat, lineCap: CGLineCap, lineJoin: CGLineJoin, miterLimit: CGFloat, dash: [CGFloat], dashPhase: CGFloat) |
引数 | 説明 | デフォルト値 |
---|---|---|
lineWidth | 線の太さ | 1 |
lineCap | 線の端の形状 | .butt |
lineJoin | 線の接続部の形状 | .miter |
miterLimit | 接続部の形状に.miterの適用を決めるしきい値 | 10 |
dash | 破線の形状を配列で指定 | [CGFloat]() |
dashPhase | 破線の開始位置 | 0 |
破線の描画(dash、dashPhase)
1 2 3 4 |
dash: [パターン配列] dashPhase: 開始位置 |
引数dashに破線の形状を次のようなパターン配列で指定します。
1 2 3 |
[線の長さ, 空白の長さ, 線の長さ, 空白の長さ・・・] |
引数dashPhaseには、パターン配列の何番目から開始するか指定します。
デフォルト値は0です。
使用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
struct ContentView: View { var body: some View { HStack(spacing: 100) { Circle() .stroke(style: StrokeStyle( lineWidth: 10, dash: [20, 20, 20, 20] )) .frame(width: 150, height: 150) Rectangle() .stroke(style: StrokeStyle( lineWidth: 10, dash: [20, 10, 5, 20] )) .frame(width: 150, height: 150) RoundedRectangle(cornerRadius: 50) .stroke(style: StrokeStyle( lineWidth: 10, dash: [5, 20, 5, 20] )) .frame(width: 150, height: 150) } } } |
線の端の形状指定(lineCap)
1 2 3 |
lineCap: 形状 |
線の端の形状をCGLineCap列挙型で指定します。
指定できる列挙子は次のいずれかです。
.butt
なにもしません。端がそのまま切り取られるイメージ。
これがデフォルトです。
.square
パスの終点を越えて線を線幅の半分の距離だけ四角く延長します。
四角く延長するので、曲線で使うと少し不自然になります。
フレームの端で使うと延長した分はみ出します。
.round
端を丸くします。
パスの終点を超えて延びるように線を描画します。
線は終点を中心に、線の幅の1/2の半径を持つ半円弧で終わります。
フレームの端で使うと丸めた分はみ出します。
使用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
struct ContentView: View { var body: some View { HStack(spacing: 100) { SampleShape() .stroke(style: StrokeStyle( lineWidth: 20, lineCap: .butt )) .frame(width: 150, height: 150) .border(Color.red) SampleShape() .stroke(style: StrokeStyle( lineWidth: 20, lineCap: .square )) .frame(width: 150, height: 150) .border(Color.red) SampleShape() .stroke(style: StrokeStyle( lineWidth: 20, lineCap: .round )) .frame(width: 150, height: 150) .border(Color.red) } } } struct SampleShape: Shape { func path(in rect: CGRect) -> Path { var path = Path() path.addArc( center: CGPoint(x: rect.maxX, y: 0), radius: rect.maxX / 2, startAngle: Angle(degrees: 90), endAngle: Angle(degrees: 180), clockwise: false ) path.move(to: CGPoint(x: rect.maxX / 4, y: 0)) path.addLine(to: CGPoint(x: rect.maxX / 4 * 3, y: rect.maxY)) path.move(to: CGPoint(x: 0, y: rect.maxY / 4 * 3)) path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY / 4 * 3)) return path } } |
破線にした場合は、次のように個々の破線の先端形状に適用されます。
接続部の形状指定(lineJoin)
1 2 3 |
lineJoin: 形状 |
線の接続部の形状をCGLineJoin列挙型で指定します。
指定できる列挙子は次のいずれかです。
.miter
鋭い(角度のついた)角を持つ接続。
パスの終点を越えた線の外側の辺を、それらが出会うまで描きます。
.round
端が丸くなっている接続
.beval
端が斜面に切り取られた接続
1 2 3 |
miterLimit: しきい値 |
.miterの適用を決めるしきい値です。
パスの終点を超えた線の長さを線幅で割った結果が、しきい値よりも大きい場合は、.bevalが採用されます。
デフォルト値は10。
使用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
struct ContentView: View { var body: some View { HStack(spacing: 100) { SampleShape() .stroke(style: StrokeStyle( lineWidth: 20, lineJoin: .miter )) .frame(width: 150, height: 150) .border(Color.red) SampleShape() .stroke(style: StrokeStyle( lineWidth: 20, lineJoin: .round )) .frame(width: 150, height: 150) .border(Color.red) SampleShape() .stroke(style: StrokeStyle( lineWidth: 20, lineJoin: .bevel )) .frame(width: 150, height: 150) .border(Color.red) } } } struct SampleShape: Shape { func path(in rect: CGRect) -> Path { var path = Path() path.addLines([ CGPoint(x: rect.maxX, y: 0), CGPoint(x: rect.maxX, y: rect.maxY), CGPoint(x: 0, y:rect.maxY), CGPoint(x: rect.maxX, y: 0), CGPoint(x: rect.maxX, y: rect.maxY) // 右上角を閉じる為に追加 ]) return path } } |
フレームサイズとの関係
.frame()モディファイアで指定したフレームサイズはあくまで元のShape部品に適用されるので、デフォルトの太さ(1)より太い線を指定した場合はフレームサイズをはみ出ます。
レイアウト調整の際には注意が必要です。
1 2 3 4 5 6 7 8 9 10 |
struct ContentView: View { var body: some View { Circle() .stroke(Color.blue, lineWidth: 20) .frame(width:200, height: 200) .border(Color.red) // フレーム枠を赤で表示 } } |
フレームにぴったりと収めたい場合は、.stroke()モディファイアの代わりに.strokeBorder()モディファイアを使用します。
線の太さを考慮してShape部品のサイズを縮小してくれます。(動作としては縦横をlineWidth/2だけ縮小したShape部品を返します)
なお、カスタムShape部品に対して使用した場合の動作は、該当部品に実装によります。
.strokeBorder()使い方は.storoke()モディファイアと基本的に同じですが、後ろに.fill()モディファイアが使えなくなるなど、若干の相違点があります。
1 2 3 4 5 6 7 8 9 10 |
struct ContentView: View { var body: some View { Circle() .strokeBorder(Color.blue, lineWidth: 20) // Modifierを置き換え .frame(width:200, height: 200) .border(Color.red) } } |