Swift Playgroundsで学ぶiOSプログラミング

ベベルギアなどさまざまな形状を描画できるUIBezierpath

文●柴田文彦 編集●吉田ヒロ

2017年02月13日 17時00分

パスの両端の形状を指定する

 パスを使った作図アプリでは、ストロークの端の形状は、さまざまなものが指定できるようになっています。アプリの場合には四角や丸以外に、矢印なども指定できるのが普通ですが、UIBezierpathには、さすがにそこまで豊富なオプションはありません。パスオブジェクトが持つlineCapStyleという属性に対して、CGLineCapという型の値が指定できるようになっています。そのCGLineCapに定義されている値は、butt、round、squareの3種類です。

 これらのうちroundは丸、squareは四角だとすぐに分かるでしょう。ではbuttはと言うと、これは「タバコの吸い殻」というようなニュアンスを含む言葉で、両端をちょん切った残りの部分のような形を表しています。水平の直線パスに、これら3種類のキャップスタイルを指定してストロークしてみましょう。元のパスとの位置関係が分かるように、パスを茶色の太い(60.0)線でストロークしたあと、白い細い(3.0)線で重ねてストロークしています。

ストロークの両端の形状を指定するlineCapStyle属性の値として、butt、round、squareの3種類を指定して直線を描いてみました

 なお今回も、前回と同じように、ビューを定義するコードの前後に、それぞれフレームワークをインポートするためと、ビューのオブジェクトを作成してプレイグラウンドに張り付けるためのコードがあります。その部分については、今回は省略するので、前回を参照してください。また、画面では、1本目のパスを描く部分しか見えていませんが、異なるのはパスのオプションと座標だけなので、2本目以降のコードも省略します。

 これらの結果を比較すると、buttは、確かに両端を容赦なく切り取ったような形で、ストロークの長さは、元のパスと同じになっています。roundは、両端にそれぞれ向き合った半円をくっつけたような形になっています。またsquqreは、正方形を半分に切って、両端に追加したような形です。なお、前回の最後に指摘したように、この画面の縦横比は1対1ではないことを思い出してください。

直線の折れ曲がり部分の形状を指定する

 次に、直線のパスが折れ曲がった部分(の外側)の形状を指定するオプションについて見てみましょう。これは、ストロークの太さを考慮した外形線を、そのまま延長するだけで良いようにも思えますが、2本の直線が鋭角に交わる場合、それではかなり尖った形になってしまいます。そこで、その尖った部分を丸めたり、削ったりするオプションが用意されているのです。

 これらのオプションは、パスオブジェクトのlineJoinStyle属性で指定します。指定可能な値は、いずれもCGLineJoin型のmiter、round、bevelです。何も指定しない場合と同じなのがmiterで、上で述べたように2本のストロークの外形線をそのまま交差するところまで延長したような形状になります。そこに丸みを付けてつなぐのが、round。平らに削るようにするのがbevelです。メカに詳しい人ならベベルギアというものをご存知かと思いますが、ちょうどそれに似た形状になります。

 2本の直線が交わるパスに、これら3種類のジョインスタイルを指定してストロークしてみましょう。

2本の直線パスの折れ曲がった部分のストロークの形状を指定するlineJoinStyle属性の値として、miter、round、bevelの3種類を指定して折れ線パスを描いてみました

 結果は、口で説明するよりも、目で見ていただくのが確実でしょう。なお、miterの場合、2本の直線が平行に近いと、交差する部分がかなり長くなってしまうという不都合があります。そのために、その長さを制限するmiterLimitというオプションも用意されています。

点線や破線、鎖線を指定するオプション

 パスを使って点線や破線、鎖線を描こうとした場合、そのためのオプションを知らなくても、途切れたパスの、それぞれの部分を別々のパスとして描いていけば不可能ではありません。しかし、それではあまりも手間がかかり過ぎます。UIBezierpathには、setLineDashというメソッドが用意されていて、パターンを指定するだけで、途切れたストロークを描くことができるようになっています。

 このメソッドの一般形は以下のようになっています。

func setLineDash(_ pattern: UnsafePointer<CGFloat>?,
count: Int,
phase: CGFloat)

 ちょっとわかりにくいかもしれませんが、最初の引数は、パターンとして、ストロークを描く部分と描かない部分の長さを配列で指定します。パターンは繰り返し使われるので、配列の要素の数は偶数になるのが普通です。パターンとして数字を2つだけ指定すれば、描く部分と描かない部分が、それぞれ同じ長さで繰り返す点線や破線になります。一点鎖線なら、描く部分と描かない部分を交互に4つ、二点鎖線なら同様に6つの数字をパターンとして指定することになります。2番目のcount引数は、そのパターンに含まれる数字の数を、3番目のphase引数は、パターンのうち、どこから描き始めるかを長さで指定します。

 やはり言葉では分かりにくいので、実際の例で確かめましょう。最初の例のパターンは[20.0, 20.0]、2番めは[100.0, 30.0, 40.0, 30.0]、3番めは[30.0, 20.0, 30.0, 20.0 120.0, 20.0]としています。また3番めだけphaseとして160.0を指定しています。これにより、パターンの数字で言うと、5番目の長く描く部分の、ちょうどまん中から描き始めることになります。

パスを途切れ途切れにストロークすることを可能にするsetLineDashメソッドを使って、破線、一点鎖線、二点鎖線を描いてみました。指定するオプションしだいで、どのようなパターンでも描けます

 なお、このような点線、破線、鎖線を構成するそれぞれのストローク部分にも、ラインキャップのオプションが有効です。ただし、roundやsquareを指定すると、その分だけ間隔が詰まってしまうので、それを考慮したパターンにする必要があります。

 また、このラインダッシュのオプションは、直線だけでなく、円弧やベジェ曲線のパスに対しても有効です。いろいろと試してみてください。

ビューの縦横比を保つためのオプション設定

 前回に指摘したように、これまでに見てきたプレイグラウンドに張り付けたビュー(UIViewオブジェクト)の縦横比は1対1になっていませんでした。そのため、円も楕円になっていました。それが気になってしかたがないという人のために、縦横比を保ったまま画面に表示するためのオプションを紹介します。それはビューのcontentModeという属性です。

 このオプションの取る値は、UIViewContentMode型のscaleToFill、scaleAspectFit、scaleAspectFillのスケール(拡縮)系と、center、top、bottom、left、right、topLeft、topRight、bottomLeft、bottomRightといった非スケール系に分かれます。デフォルトの設定はscaleToFillで、縦横比を無視して、ビューが画面(プレイグランドの場合は右側のグラフィック画面)いっぱいになるように拡大または縮小してはめ込みます。縦横比を保ったまま拡縮するのは、scaleAspectFitとscaleAspectFillです。前者は、ビュー全体が画面に収まるようにしますが、画面と縦横比が異なる場合は、隙間が発生します。後者は縦横どちらかぴったり収まるようにするので、隙間はできない代わり、画面からはみ出す部分がでてきます。

 前回のドーナッツ型を描くプログラムのビューにscaleAspectFitを指定して表示してみましょう。

UIViewのオブジェクトのcontentMode属性にscaleAspectFitを指定して、ビューの縦横比を保ちつつ、ビュー全体が画面に収まるように表示してみました

 円は真円になりましたが、上下に隙間ができて黒くなっています。iPadを縦長に持って、プレイグラウンドのグラフィック画面を横長にすると、こんどは左右に隙間ができてしまいます。

前の図と同じプログラムで、iPadを縦長に持って表示してもビューの縦横比は変わりません。しかし左右に隙間ができています

 一方、今回の折れ線のプログラムにscaleAspectFillを指定してみると、ビューが画面からはみ出している様子が確認できます。

ビューのcontentMode属性にscaleAspectFillを指定すると、ビューの縦横比は保たれますが、ビューが画面からはみ出す部分ができてしまいます

 iOSアプリを作成する場合は、縦横比が変わってしまうのも困りますが、画面とビューの間に隙間ができたり、はみ出す部分が出てきても困ります。ということは、また別のアプローチが必要になるというわけです。それについては、また別の機会に改めて考えましょう。

次回の予定

 ベジェ曲線で描くグラフィックは、iOSプログラミングの重要な基本の1つですが、そろそろ飽きてきたころでしょう。次回からは、今回まで使ってきた、いわば「素」のビューではなく、はっきりした目的を持って開発されたビューを扱っていくことにしましょう。そのためには、また新たなフレームワークのインポートが必要となります。

mobileASCII.jp TOPページへ