リアルタイムに描画結果を確認
Xcode6で追加されたInterfaceBuilder(以下IB)の新しい機能であるIBDesignableとIBInspectableを使ってみました。
特徴としては細かなパラメータを設定できて、それを画面上で確認できることです。
以下の画像はIBDesignableを利用した円形のグラデーションをレンダリングするカスタムViewを使用して、IB上でパラメータを調整して作ったものです。
グラデーションの色、グラデーションの開始地点などをIBで設定しています。GitHubにプロジェクトファイルをあげています。
Autolayout対応がマストになった今、いろいろなサイズでの見た目を簡単に調整できた方が楽ですよね。
以前はこういったカスタムViewの細かな調整はコードを修正しながら行っていましたが、ちょっと工夫すればIB上で簡単に変更できるようになります。
@IBDesignable
を利用したグラデーションビュー
IB上でグラデーションの調整をできるようにしたGradationView
のソースです。
@IBDesignable class GradationView: UIView {
@IBInspectable var gradationStartColor : UIColor = UIColor.whiteColor()
@IBInspectable var gradationEndColor : UIColor = UIColor.blackColor()
@IBInspectable var gradationXPosition : CGFloat = 0.5
@IBInspectable var gradationYPosition : CGFloat = 0.5
@IBInspectable var circleSize : CGFloat = 1.0
override func drawRect(rect: CGRect) {
var startRed: CGFloat = 0
var startGreen: CGFloat = 0
var startBlue: CGFloat = 0
var startAlpha: CGFloat = 0
self.gradationStartColor.getRed(&startRed, green: &startGreen, blue: &startBlue, alpha: &startAlpha)
var endRed: CGFloat = 0
var endGreen: CGFloat = 0
var endBlue: CGFloat = 0
var endAlpha: CGFloat = 0
self.gradationEndColor.getRed(&endRed, green: &endGreen, blue: &endBlue, alpha: &endAlpha)
var context = UIGraphicsGetCurrentContext()
CGContextSaveGState(context)
CGContextAddEllipseInRect(context, self.frame)
var colorSpaceRef:CGColorSpaceRef = CGColorSpaceCreateDeviceRGB()
var components:[CGFloat] = [
startRed, startGreen, startBlue, startAlpha,
endRed, endGreen, endBlue, endAlpha,
]
var locations:[CGFloat] = [ 0.0, 1.0 ]
let locationCount = UInt(locations.count)
var gradientRef: CGGradientRef = CGGradientCreateWithColorComponents(colorSpaceRef, components, locations, locationCount)
var frame:CGRect = self.bounds
var radius:CGFloat = CGRectGetWidth(frame)
var startCenter: CGPoint = frame.origin
startCenter.x += frame.size.width * self.gradationXPosition
startCenter.y += frame.size.height * self.gradationYPosition
var endCenter = startCenter
var startRadius: CGFloat = 0.0
var endRadius : CGFloat = CGRectGetWidth(self.bounds) * self.circleSize
CGContextDrawRadialGradient(context, gradientRef, startCenter, startRadius, endCenter, endRadius, CGGradientDrawingOptions(kCGGradientDrawsAfterEndLocation))
CGContextRestoreGState(context)
}
}
@IBDesignable
をクラス名の前につけるとIB上で@IBInspectable
をつけたプロパティの変更とその表示ができるようになります。
@IBDesignable
はUIViewのサブクラスに、@IBInspectable
はプロパティに設定します。@IBInspectable
に設定できる型については後述します。
ビルド時
@IBDesignable
を設定したViewをStoryboard、あるいはxibに設定すると、identity inspectorの欄に"Designable"という項目が追加されます。
ステータスとしては更新中を示すUpdating
、更新済みを示すUp to date
、ビルド失敗を示すBuild failed
の3つを確認しています。
ビルド失敗の場合はissue navigatorに警告が出ます。
@IBInspectable
に指定できる型
@IBInspectable
を使用できる型は決まっています。
- Bool
- Int
- CGFloat
- Double
- String
- CGPoint
- CGSize
- CGRect
- UIColor
- UIImage
設定するとIB上ではこのように表示されます。
コード上で初期値として設定した値が代入されるわけではないようです。
CGRect
やCGSize
、CGPoint
などの実数を指定する項目の数値のインクリメント、デクリメントが1.0ずつステップしてしまって困っているのですが設定方法がわかりません。しょうがないので実数を手入力しています。
IB使用時のみ有効にする
IBだけで確認するためのメソッド、マクロも用意されています。
IBだけに反映させたいものがある場合は、prepareForInterfaceBuilder()
メソッドを利用します。
func prepareForInterfaceBuilder()
マクロはTARGET_INTERFACE_BUILDER
を使用します。
override func drawRect(rect: CGRect) {
#if TARGET_INTERFACE_BUILDER
// IBで作業時に描画させたいもの
#endif
}
まとめ
少しこったイメージのUIを作ろうとしたときはUIImageViewを利用すればできるのですが、修正を入れるたびに画像に手を加えてプロジェクト内に取り込む作業をしなければならないのが面倒です。@IBDesignable
を使えばコードの修正とIBをちょっといじるだけでリアルタイムに結果を確認できるのはすごく便利ですね。
@IBDesginable
を使うことでリアルタイムに確認ができるので、ビルドしては確認し、ビルドしては確認し、というプロセスを繰り返さなくても良くなります。@IBDesignable
を使用したカスタムViewを作って、細かな設定はデザイナーさんにお任せしてしまう、というのもありかもしれません。
メモ
WWDC2014のWhat's New in Interface Builderではカスタムフレームワークを作成するように言っていたのですが、作らなくても使用可能でした。カスタムフレームワークについて知識不足なので、この点はもう少し調べて追記しようと思います。