2014/11/20

@IBDesignableと@IBInspectableでストーリーボード上に表示されるビューをつくる

Xcode 6からインタフェースビルダ (ストーリーボード) 向けに2つの修飾子@IBDesignable@IBInspectableが追加されました。

この2つを使うことでストーリーボード上で実際に描画されたビューを見たり、ビューの属性を変更したりできるようになるので、試してみました。

なお、WWDC 2014のセッション「What’s new in Xcode 6」内の解説が詳しいです。

簡単な紹介

とりあえず使ってみましょう。

UIViewを継承したクラスを作成して、それに@IBDesignableを付けます。 また、インスタンス属性を作成して、それには@IBInspectableを付けます。

@IBDesignable class ShapeView: UIView {

    @IBInspectable var cornerRadius: CGFloat {
        get { return self.layer.cornerRadius }
        set { self.layer.cornerRadius = newValue }
    }

    @IBInspectable var shapeBackgroundColor: UIColor? {
        get { return UIColor(CGColor: self.layer.backgroundColor) }
        set { self.layer.backgroundColor = newValue?.CGColor }
    }
}

次に、UIViewをストーリーボードに貼り付けてから、そのCustom ClassをShapeViewにします。

すると、Desinablesという項目が出現して、その値が「Up to date」になります。Up to dateは最新のコードと同じ表示がなされているということを意味しています。

Attributesインスペクタに移動すると@IBInspectableで指定したプロパティが出現しています。

ここの値を変更すると、ストーリーボード上での表示もすぐに反映されます。

注意事項

現在のところ、@IBDesignable@IBInspectableには次のような制限があるみたいです。

  • @IBDesignableになるには、UIViewNSViewの直接のサブクラスである必要がある (UITableViewCellを@IBDesignableにしても表示されない)。
  • @IBInspectableになれる型はUser Defined Runtime Attributesと同じ。enumは利用できない。
  • 画像などを表示する必要があって、とりあえずプレースホルダを設定させたいときなどはprepareForInterfaceBuilderを使うことができる (後の例では利用しています)
  • WWDCではウィジェット用のフレームワークを作る必要があるように言っているが、少なくともSwiftでは不要っぽい

なお、@IBInspectableで設定した値は、IdentityインスペクタのUser Defined Runtime Attributesにも表示されるようになります。

なので、実は@IBInspectableはUser Defined Runtime Attributesよりちょっと楽に値が設定できるだけだったりします。

ウィジェットのデバッグ

  1. そのウィジェットのブレークポイントを設置
  2. ウィジェットのストーリーボード上で選択
  3. メニューから、Edit → Debug Selected Views

複雑な例

サブビューがあっても@IBDesignableが有効かどうかを試してみました。

@IBDesignable class EditTableViewCell: UIView {

    let titleLabel = UILabel()
    let textField = UITextField()

    @IBInspectable var title: String {
        get { return titleLabel.text! }
        set { titleLabel.text = newValue }
    }

    @IBInspectable var placeholder: String? {
        get { return textField.placeholder }
        set { textField.placeholder = newValue }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }

    override func prepareForInterfaceBuilder() {
        if titleLabel.text == nil {
            title = "titleを入力してください"
        }
        if textField.placeholder == nil {
            placeholder = "placeholderを入力してください"
        }
    }

    private func setupView() {
        addSubview(titleLabel)
        addSubview(textField)
        titleLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
        textField.setTranslatesAutoresizingMaskIntoConstraints(false)
        addConstraints(layoutConstraints())
    }
}

試しにテーブルビューの静的表示でセルに貼り付けてみました。プロパティに値をセットしていないので、prepareForInterfaceBuilderで指定した値が表示されています。

関連リンク

紹介したコードは次のリポジトリに上げています。

0 件のコメント:

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。