XcodeのLive Renderingを使って簡単に使い勝手の良いアイコンボタンをつくろう

December 09, 2014

こんばんは。Xcode6になって様々な機能がXcodeに追加されました。その中で、作業的にかなりやりやすくなったLive Renderingについて書いてみたいと思います。とても便利なのにもしかしたらあまり使われていないのかもと思ったからです。Live Renderingとは、UIViewで書いた内容がInterfaceBuilder上にリアルタイムに反映されるというとても便利な機能です。入り込みやすいようにチュートリアル形式にしました。

Live Renderingを利用することでの変化

今まではカスタムのViewをInterfaceBuilderで表示する際、中身がリアルタイムに描画されないので、下記のようになってしまっていました。もしくは、Viewに色を付けずタップしないとどこにカスタムのビューがあるかわからないような状況が発生していました。

View

それがLive Renderingを利用することで、このように実際に端末に表示された時のようにアイコンを見ることができるようになり、より一歩踏み込んだビューの作成ができると思います

View2

それでは実際にLive Renderingを利用したボタンを作ってみたいと思います。

簡単にアイコンボタンを作る

UIButtonを継承したクラスを利用してもよいのですが、もっと自由にやりたいのでここでは、UIControlを継承したクラスを作成します。そして、下記のコードを書き込みます。 @IBDesignable をクラス宣言の前に書くことで、Live Renderingが有効になります。 @IBInspectable をインスタンス変数の前に書くことで、InterfaceBuilderから値を変更することができるようになります。ここで、

@IBDesignable class IconButton: UIControl {

@IBInspectable var iconImage: UIImage?

override func drawRect(rect: CGRect) {
    
    if let iconImage = self.iconImage {
        iconImage.drawInRect(self.bounds)
        
        let maskPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: CGRectGetWidth(self.bounds) / 2)
        let maskLayer = CAShapeLayer()
        maskLayer.path = maskPath.CGPath
        self.layer.mask = maskLayer;
    }
}

}

InterfaceBuilderを開き、UIViewを画面に追加します。そして、クラスを作成したIconButtonに変更します。

Class

すると、InterfaceBuilderでこのような表示を見ることができます。IconButtonで設定したiconImageがInterfaceBuilderから変更できるようになっています。

Icon image

今回は下記の画像を利用することにしました。

Icon 2x

今の時点でInterfaceBuilderはこのようになっています。

Now

ここまでのコードは、こちらを御覧ください

ちょっとさみしいので、枠線を追加したいと思います。

@IBDesignable class IconButton: UIControl {

@IBInspectable var iconImage: UIImage?
@IBInspectable var borderColor: UIColor = UIColor.clearColor()
@IBInspectable var lineWidth: CGFloat = 0.0
var borderLayer: CAShapeLayer?

override func drawRect(rect: CGRect) {
    
    if let iconImage = self.iconImage {
        iconImage.drawInRect(self.bounds)
    }
    
    let path = UIBezierPath(roundedRect: self.bounds, cornerRadius: CGRectGetWidth(self.bounds) / 2)
    let maskLayer = CAShapeLayer()
    maskLayer.path = path.CGPath
    self.layer.mask = maskLayer;
    
    if self.borderLayer == nil {
        self.borderLayer = CAShapeLayer()
        self.borderLayer!.path = path.CGPath
        self.borderLayer!.fillColor = UIColor.clearColor().CGColor
        self.layer.addSublayer(self.borderLayer!)
    }
    self.borderLayer!.strokeColor = self.borderColor.CGColor
    self.borderLayer!.lineWidth = self.lineWidth
    self.borderLayer!.lineCap = kCALineCapRound
}

}

View2

attributes Inspectorはこのようになっています。

Attributes

ボタンの要素を幾つか追加して簡単にアイコン・ボタンを作ることが出来ました。

更に、アクションやhighlightedの時の変化などを付け加えて、より使い勝手の良いボタンにしました。

@IBDesignable class IconButton: UIControl {

@IBInspectable var normalImage: UIImage? {
    didSet {
        self.setNeedsDisplay()
    }
}

@IBInspectable var highlightedImage: UIImage? {
    didSet {
        self.setNeedsDisplay()
    }
}

@IBInspectable var normalBorderColor: UIColor = UIColor.clearColor() {
    didSet {
        self.setNeedsDisplay()
    }
}

@IBInspectable var highlightedBorderColor: UIColor = UIColor.clearColor() {
    didSet {
        self.setNeedsDisplay()
    }
}

@IBInspectable var lineWidth: CGFloat = 0.0 {
    didSet {
        self.setNeedsDisplay()
    }
}

lazy var maskLayer: CAShapeLayer = {
    return CAShapeLayer()
}()

lazy var borderLayer: CAShapeLayer = {
    let borderLayer = CAShapeLayer()
    borderLayer.fillColor = UIColor.clearColor().CGColor
    self.layer.addSublayer(borderLayer)
    return borderLayer
}()

var circlePath: UIBezierPath {
    get {
        return UIBezierPath(roundedRect: self.bounds, cornerRadius: CGRectGetWidth(self.bounds) / 2)
    }
}

override var highlighted: Bool {
    didSet {
        self.setNeedsDisplay()
    }
}

override func drawRect(rect: CGRect) {
    
    var iconImage: UIImage?
    var borderColor = UIColor.clearColor()
    if self.highlighted {
        if let highlightedImage = self.highlightedImage {
            iconImage = highlightedImage
            borderColor = self.highlightedBorderColor
        }
    } else {
        if let normalImage = self.normalImage {
            iconImage = normalImage
            borderColor = self.normalBorderColor
        }
    }
    
    iconImage?.drawInRect(self.bounds)
    
    self.maskLayer.path = self.circlePath.CGPath
    self.layer.mask = self.maskLayer
    
    self.borderLayer.path = self.circlePath.CGPath
    self.borderLayer.strokeColor = borderColor.CGColor
    self.borderLayer.lineWidth = self.lineWidth
}

}

import UIKit

class ViewController: UIViewController {

@IBAction func buttonTapped(sender: IconButton) {
    println("button tapped!")
}

}

おまけ:利用画像をベクターで

この時点で、どんな形に伸縮しても縦横が同じサイズであればその矩形の大きさのアイコンボタンを作ることが出来ました。ただし、現在、100px x 100pxの表示をするために2倍スケールの200px x 200px の画像を利用していています。このままではiPhone 6Plusのために別に3倍スケールの画像を用意する必要があります。別画像を用意するのではなく、1つの画像を用意するだけで3倍スケールにも対応できるようにvectorを利用しようと思います。

先ほどの画像ファイルは元はIllustratorで作成したものでした。これをIllustratorで開きます。

Illustrator

ファイル>複製を保存… を選択し、PDF形式で保存します。

Illustrator save

ダイアログにはこのように返答しました。

Illustrator dialog

ImageAssetsのTypesをVectorsに変更します。

Assets

これでVector画像を扱えるようになりました。今回は100px x 100px のpdfを書き出してセットしています。ビルドをする際にXcodeがscale毎のビットマップ画像((@1x, @2x, @3x))を出力してくれるということです。

Live Renderingを利用することで、開発が更に楽しくなると思っています!ちなみに最後になりましたが、この機能はObjective-Cでも利用できます。

サンプルコード

一通り実装したサンプルコードはこちらになります。

参考


Profile picture

Written by morizotter who lives and works in Tokyo building useful things. You should follow them on Twitter