Interface Builderで一度配置したビューを別のクラスに置き換えたいことがたまにあります。自作のサブクラスに置き換えるのであれば、クラスの指定を変更すれば済みますが、UIView
をUIImageView
に置き換えたいときなど、Interface Builderでやるには再度配置するしかなく、特にAuto Layoutの設定をしてしまった後だと非常に面倒な作業になります。
これを避けたければ中身のXMLを直接編集するしかありません(個人的にこれをxibの開腹手術と呼んでいます)。
UIViewをUIImageViewに置き換える
まず簡単な例としてUIView
をUIImageView
に置き換えることを考えます。
単純にクラスの指定をUIImageView
に変更するのも手ではあります。しかしこれではIB上での扱いはただのUIView
と変わらず、image
の設定などができません。
ここではXMLを編集することでクラスを置き換えてみます。と言ってもXMLの書式を把握しているわけではないので、まずは置き換えたいクラスUIImageView
を配置してみます。
そしてxib(またはStoryboard)ファイルをXMLとして開きます。そのためには左カラムのProject Navigatorから目的のファイルを右クリックしてOpen As > Source Codeを選択します。
表示されたXMLから、置き換え元のクラスと置き換え先のクラスがどこにあるか探してください。要素が多くて探しにくい場合はviewについたID(右カラムのIdentity inspectorにObject IDとして表示されます)で検索すると楽です。
ここで重要なのは「id」と「frame」と「constraints」です。
これらの要素をUIImageView
側にコピーして、UIView
の方は削除します。
再びProject Navigatorからファイルを右クリックし、Open As > Interface Builder XIB Documentを選択して、IBで開きます。
問題がなければ下図のように期待通りの結果が得られます。XMLの編集を間違えると最悪ファイルを開くたびにXcodeがクラッシュするので注意してください。
UITableViewCellをUICollectionViewCellに置き換える
より複雑な例としてUITableViewCell
をUICollectionViewCell
に置き換えることを考えます。セルの上に配置した要素はそのままに、根元だけクラスを変更します。複雑なビュー階層とAuto Layoutを設定していた場合、これを最初から作り直すことは避けたいでしょう。
ここではわかりやすくするため、以下のようにcontentView
直下にビューがひとつ置かれている単純な構造を考えます。
TableViewCellのXMLは以下のようになっています。
<?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は以下のようになっています。
<?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では構造が少し違っているのがわかります。
これを参考に、以下の手順でTableViewCellからCollectionViewCellへの移植が可能です。
-
collectionViewCell
のIDをtebleViewCellContentView
のIDに合わせる -
tebleViewCellContentView
の下のsubviews
をcollection viewの<view key="contentView">
の下にコピー -
tebleViewCellContentView
の下のconstraints
をcollectionViewCell
の下にコピー
collection viewではsubviewとconstraintの置かれている階層が違うことに注意が必要です。