(注:数回後の投稿で行なった実験が終わったので、ソースコードの一部を修正しました。修正後の現状が正式版です)
オフスクリーン描画機能をライブラリ化する話の続きです。いよいよSwiftでソースコードを作る段階です。大まかな構造は決まっていますから、swiftだと簡単に作れます。ライブラリ用のクラスとして実現するので、クラス内に配列を用意して、CGContextで使う色設定を保存する必要があります。
まずは入れ物となるクラスを作ります。クラス名は少し長めですが、DrawImageOffScreenとしました。オフスクリーン以外の描画ライブラリも作ると予想し、DrawImageシリーズとして統一しようと思っての命名です。
クラス内には配列を用意して、CGContextの色設定を保存する必要があります。線色(StrokeColor)と塗りつぶし色(FillColor)の両方を。また、それぞれの色の初期値も先頭で定義しておくべきでしょう。Swiftコードは、次のようになります。
// オフスクリーン描画機能をライブラリ化したクラス
class DrawImageOffScreen {
// 色のデフォルト値
let COLOR_DFAULT_STROKE : [CGFloat] = [0.0, 0.0, 0.0, 1.0] // 黒で不透明
let COLOR_DFAULT_FILL : [CGFloat] = [1.0, 1.0, 1.0, 1.0] // 白で不透明
// CGContextに設定する値の入れ物
var numContext : Int = 0
var arySColor : [[CGFloat]] = [] // StrokeColor
var aryFColor : [[CGFloat]] = [] // FillColor
// 描画可能フラグ
var enabledDraw : Bool = false
}
色の初期値としては、線色が黒の不透明、塗りつぶし色は白の不透明にしました。中を塗りつぶさない場合の指定は、図形を描く命令で可能なので、中を塗りつぶす場合のデフォルト値として白を選びました。
配列の要素数を示す変数名は、numContextとしました。今は色情報だけですが、他の情報も加えたとき、変数名を変更しなくて済むようにとの配慮です。
さらに、描画可能状態かを示すフラグenabledDrawも加えます。これがtrueのときだけ、設定や描画を可能とします。
このクラスの中に、いろいろなメソッドを追加することになります。
最初は、クラスの初期化です。できるだけ少ない行数でクラスを使いたいので、開始に必要な値を初期化に含めています。イメージの大きさ(幅と高さ)、背景の透明不透明、使用する色数だけです。
入れるかどうか迷ったのが、UIGraphicsBeginImageContextWithOptionsの引数scaleです。自動判定を意味する0.0のまま使い続けそうなので、今回は外しました。必要になった時点で入れればよいとの判断です。
// サイズを指定して、空のイメージを生成する
init(_ rWidth:CGFloat, _ rHight:CGFloat, _ rOpaque:Bool, _ rNumContext:Int) {
if (rNumContext < 1) { println("ERROR:DIOS_IN:rNumContextが小さすぎ"); return }
// コンテキストのデフォルト値を設定
numContext = rNumContext
for i in 0..<rNumContext {
arySColor.append(COLOR_DFAULT_STROKE)
aryFColor.append(COLOR_DFAULT_FILL)
}
// オフスクリーン描画の開始(rOpaqueがfalseで背景が透明に,scaleが0.0で自動)
let iCGSize : CGSize = CGSizeMake(rWidth, rHight)
UIGraphicsBeginImageContextWithOptions(iCGSize, rOpaque, 0.0)
enabledDraw = true // 描画可能に設定
}
ここでも「色のデフォルト値を設定」ではなく、「コンテキストのデフォルト値を設定」としました。色以外の情報を追加したとき、コメントを修正しなくて済むようにとの配慮です。
一番最初に、最低限のエラーチェックを入れいています。rNumContextに1より小さい値を指定した場合は、エラーメッセージを出し、何もしないで終わります。
エラーメッセージのフォーマットは以前からほぼ固定です。必ず「ERROR:」で始まり、エラーを出したクラス名の省略形と「_」が続き、その中の場所を示す省略形と「:」が加わります。最後にエラー内容を示す文字列を付けるという、ありがちな形式です。今回の場合は、「DrawImageOffScreen」の省略形が「DIOS」で、「init」の省略形が「IN」ですね。
続いて、初期化とペアになる終了処理を書きます。描いた画像をUIImegeとして返し、描画処理を終了するメソッドです。処理内容は非常に簡単で、次のようなSwiftコードになります。
// 出来上がった合成イメージを返す
func getFinalImageF() -> UIImage {
if !enabledDraw { println("ERROR:DIOS_GI:Disabled"); return nil }
// 描き終わった内容をUIImageに保存する
let iImage : UIImage = UIGraphicsGetImageFromCurrentImageContext()
// 描画の終了
enabledDraw = false // 描画不可能に設定
UIGraphicsEndImageContext()
return iImage
}
処理内容に関しては、とくに説明する必要はないでしょう。最初に描画可能かどうか調べて、可能なら処理を進めます。最後に、描画不可能に設定してから、コンテキストを終了してます。
迷ったのはメソッド名ですね。このメソッドには、2つの役割が含まれます。出来上がったUIImageをゲットする役割と、描画機能を終了する役割です。ゲットのほうを重視しながら、終わりを意味する「Final」を入れてみました。イマイチですね。
ここでは2つの役割を1つのメソッドに含めましたが、別々のメソッドとして作るという選択肢もあります。別々に作った場合の短所は、描画処理全体の最後に、余計なメソッドを呼ぶ必要があることです。逆に長所は、途中まで描いた段階でもUIImageを取得できることです。途中の段階と最終段階の両方をUIImageで得たい処理のとき、無駄な手間が不要となります。実際には小さい違いなので、好きな方を選んで構わないと思います。
次は、CGContextに設定するための色を指定するメソッドです。色には、線色と塗りつぶし色があり、それぞれ透明度まで含めると4つずつの値が含まれます。それを一気に指定すると、多すぎる引数となります。また、線を描く場合などは、線色しか使いません。やはり、別々のメソッドとして作るほうが無難でしょう。
そうして作ったSwiftコードは、次のとおりです。
// コンテキストの線色を設定する
func setContextStrokeColorF(rNum:Int, _ rRed:CGFloat, _ rGreen:CGFloat, _ rBlue:CGFloat, _ rAlpha:CGFloat) {
if !enabledDraw { println("ERROR:DIOS_SC:Disabled"); return }
if (rNum >= numContext || rNum < 0) { println("ERROR:DIOS_SC:rNumが範囲超え"); return }
arySColor[rNum][0] = rRed
arySColor[rNum][1] = rGreen
arySColor[rNum][2] = rBlue
arySColor[rNum][3] = rAlpha
}
// コンテキストの塗りつぶし色を設定する
func setContextFillColorF(rNum:Int, _ rRed:CGFloat, _ rGreen:CGFloat, _ rBlue:CGFloat, _ rAlpha:CGFloat) {
if !enabledDraw { println("ERROR:DIOS_FC:Disabled"); return }
if (rNum >= numContext || rNum < 0) { println("ERROR:DIOS_FC:rNumが範囲超え"); return }
aryFColor[rNum][0] = rRed
aryFColor[rNum][1] = rGreen
aryFColor[rNum][2] = rBlue
aryFColor[rNum][3] = rAlpha
}
どちらのメソッドでも、描画可能かどうかと、配列の範囲を超えてないかだけはチェックしています。エラーメッセージはほとんど同じですが、それぞれのメソッドを示す省略形だけが違っています。
4つの値のチェックも入れるべきか迷いましたが、入れないことにしました。表示された色を確認しながら開発するはずなので、間違えたら気付くだろうと思ったからです。また、値が範囲を超えても、異常終了したりせず、超えた分を無視して処理されるからです。たとえば、1.0を超えた値を指定すると、1.0として処理されます。
このままだと配列の要素数が、最初に指定したまま固定になっています。使う側の処理内容によっては、どれだけ使うのか途中で判断し、追加しながら使いたい場合もあるでしょう。
そこで、配列を追加するメソッドだけを加えました。追加する個数を指定する形にしてます。次のように、Swiftコードは非常に簡単です。
// 指定された個数だけコンテキストを追加する
func addContextF(rNumAdd:Int) {
if !enabledDraw { println("ERROR:DIOS_AC:Disabled"); return }
numContext += rNumAdd
for _ in 1...rNumAdd {
arySColor.append(COLOR_DFAULT_STROKE)
aryFColor.append(COLOR_DFAULT_FILL)
}
}
このメソッドでコンテキストを追加した後、追加した数の分だけ、前述の色設定メソッドを使って、線色と塗りつぶし色を登録することになります。正しく使っているか確認する意味で、描画可能かどうかもチェックしています。
配列から削除する処理は面倒なので作りませんでした。追加があるだけでも便利に使えますし、イメージを作り終わったらメモリーを開放するでしょうから、削除が無くても困らないと考えました。
ここまでで、描画を担当するメソッド以外は作り終えました。このまま続けると、さらに長くなりそうなので、ここで一旦切ります。残りのメソッドは、次回ということで。
(使用開発ツール:Xcode 6.0.1, SDK iOS 8.0)
0 件のコメント:
コメントを投稿