Help us understand the problem. What is going on with this article?

@IBDesignableと@IBInspectableを使ってグラデーション可能なカスタムViewを作ってみた

More than 3 years have passed since last update.

リアルタイムに描画結果を確認

Xcode6で追加されたInterfaceBuilder(以下IB)の新しい機能であるIBDesignableとIBInspectableを使ってみました。

特徴としては細かなパラメータを設定できて、それを画面上で確認できることです。

以下の画像はIBDesignableを利用した円形のグラデーションをレンダリングするカスタムViewを使用して、IB上でパラメータを調整して作ったものです。

スクリーンショット 2015-01-29 13.31.01.png

グラデーションの色、グラデーションの開始地点などを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"という項目が追加されます。

ib_updating.png

ステータスとしては更新中を示すUpdating、更新済みを示すUp to date、ビルド失敗を示すBuild failedの3つを確認しています。

ib_upToDate.png

ib_failed.png

ビルド失敗の場合はissue navigatorに警告が出ます。

issue_nav.png

@IBInspectableに指定できる型

@IBInspectableを使用できる型は決まっています。

  • Bool
  • Int
  • CGFloat
  • Double
  • String
  • CGPoint
  • CGSize
  • CGRect
  • UIColor
  • UIImage

設定するとIB上ではこのように表示されます。

IBInspectable.png

コード上で初期値として設定した値が代入されるわけではないようです。

CGRectCGSizeCGPointなどの実数を指定する項目の数値のインクリメント、デクリメントが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ではカスタムフレームワークを作成するように言っていたのですが、作らなくても使用可能でした。カスタムフレームワークについて知識不足なので、この点はもう少し調べて追記しようと思います。

参考

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away