Posted at

Interface Builderで配置したビューのクラスを後から変更する方法

More than 3 years have passed since last update.

Interface Builderで一度配置したビューを別のクラスに置き換えたいことがたまにあります。自作のサブクラスに置き換えるのであれば、クラスの指定を変更すれば済みますが、UIViewUIImageViewに置き換えたいときなど、Interface Builderでやるには再度配置するしかなく、特にAuto Layoutの設定をしてしまった後だと非常に面倒な作業になります。

これを避けたければ中身のXMLを直接編集するしかありません(個人的にこれをxibの開腹手術と呼んでいます)。


UIViewをUIImageViewに置き換える

まず簡単な例としてUIViewUIImageViewに置き換えることを考えます。

IBTips201.png

単純にクラスの指定をUIImageViewに変更するのも手ではあります。しかしこれではIB上での扱いはただのUIViewと変わらず、imageの設定などができません。

IBTips202.png

ここではXMLを編集することでクラスを置き換えてみます。と言ってもXMLの書式を把握しているわけではないので、まずは置き換えたいクラスUIImageViewを配置してみます。

IBTips203.png

そしてxib(またはStoryboard)ファイルをXMLとして開きます。そのためには左カラムのProject Navigatorから目的のファイルを右クリックしてOpen As > Source Codeを選択します。

IBTips204.png

表示されたXMLから、置き換え元のクラスと置き換え先のクラスがどこにあるか探してください。要素が多くて探しにくい場合はviewについたID(右カラムのIdentity inspectorにObject IDとして表示されます)で検索すると楽です。

ここで重要なのは「id」と「frame」と「constraints」です。

IBTips205.png

これらの要素をUIImageView側にコピーして、UIViewの方は削除します。

IBTips206.png

再びProject Navigatorからファイルを右クリックし、Open As > Interface Builder XIB Documentを選択して、IBで開きます。

問題がなければ下図のように期待通りの結果が得られます。XMLの編集を間違えると最悪ファイルを開くたびにXcodeがクラッシュするので注意してください。

スクリーンショット 2015-08-04 22.50.25.png


UITableViewCellをUICollectionViewCellに置き換える

より複雑な例としてUITableViewCellUICollectionViewCellに置き換えることを考えます。セルの上に配置した要素はそのままに、根元だけクラスを変更します。複雑なビュー階層とAuto Layoutを設定していた場合、これを最初から作り直すことは避けたいでしょう。

ここではわかりやすくするため、以下のようにcontentView直下にビューがひとつ置かれている単純な構造を考えます。

IBTips208.png

TableViewCellのXMLは以下のようになっています。


TableViewCell.xib

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14E46" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="zdF-YS-G3Z">
<rect key="frame" x="0.0" y="0.0" width="320" height="88"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="zdF-YS-G3Z" id="3eA-Vz-neI">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="6HF-V6-sxv" userLabel="Image View">
<rect key="frame" x="8" y="8" width="72" height="72"/>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="72" id="8lk-Uc-lxx"/>
<constraint firstAttribute="width" constant="72" id="G96-X2-EQ8"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstItem="6HF-V6-sxv" firstAttribute="top" secondItem="3eA-Vz-neI" secondAttribute="top" constant="8" id="PlM-tb-ObS"/>
<constraint firstItem="6HF-V6-sxv" firstAttribute="leading" secondItem="3eA-Vz-neI" secondAttribute="leading" constant="8" id="XlX-fl-gER"/>
</constraints>
</tableViewCellContentView>
<point key="canvasLocation" x="271" y="133"/>
</tableViewCell>
</objects>
</document>

一方、CollectionViewCellは以下のようになっています。


CollectionViewCell.xib

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14E46" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="KEu-Vi-azp">
<rect key="frame" x="0.0" y="0.0" width="320" height="88"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="320" height="88"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Zjc-6s-snt" userLabel="Image View">
<rect key="frame" x="8" y="8" width="72" height="72"/>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" constant="72" id="VjM-p5-jxX"/>
<constraint firstAttribute="height" constant="72" id="qmW-i8-JdR"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<constraints>
<constraint firstItem="Zjc-6s-snt" firstAttribute="leading" secondItem="KEu-Vi-azp" secondAttribute="leading" constant="8" id="3JU-Lw-Ko2"/>
<constraint firstItem="Zjc-6s-snt" firstAttribute="top" secondItem="KEu-Vi-azp" secondAttribute="top" constant="8" id="nW8-az-cXE"/>
</constraints>
<point key="canvasLocation" x="253" y="188"/>
</collectionViewCell>
</objects>
</document>

上記ではわかりにくいので、重要な部分だけ抜き出してみると以下のようになります。TableViewCellとContentViewCellでは構造が少し違っているのがわかります。

IBTips210.png

IBTips209.png

これを参考に、以下の手順でTableViewCellからCollectionViewCellへの移植が可能です。



  1. collectionViewCellのIDをtebleViewCellContentViewのIDに合わせる


  2. tebleViewCellContentViewの下のsubviewsをcollection viewの<view key="contentView">の下にコピー


  3. tebleViewCellContentViewの下のconstraintscollectionViewCellの下にコピー

collection viewではsubviewとconstraintの置かれている階層が違うことに注意が必要です。