Terrarium

いわゆる掃き溜めの ありふれた有象無象

Storyboardとxibを使ってViewを小分けで管理する

はじめに

iOS(に限りませんが)でアプリ開発をしている際に,画面にでてくるコンポーネントをそれぞれ個別で設定したい場合がほとんどです. 画面内に複数のViewがある際に,それぞれのViewをxibで記載し,それをStoryboardにまとめる,という方法を使うことでViewの管理が楽になりそうなのでメモとして残しておきます.

TL;DR

  • アプリ内の各画面はStoryboardで管理
  • 細かいViewはxibでデザインを,UIViewクラスでoutlet経由でViewの見た目を管理する
    • File's Ownerに適切にclassを設定する
  • StoryboardにViewコンポーネントを配置し,Custom Classを設定
  • ViewControllerにViewをOutletで紐付けすることで,Viewの管理が可能

方法

1. View1の要素をxibで作成する

「NewFile」から「View」を選ぶとxibファイルが作成できます.

f:id:tellusium:20180927093328p:plain
Viewを選ぶとxibファイルが作成できる

このViewはラベルが2つあるので,それぞれConstraintをつけつつ配置します. 名前はTopView.xibとしました.

f:id:tellusium:20180927093439p:plain
ラベルを2つ配置したxib

ちなみにSimulated MetricsのSizeをFreedomにするとこの表示上でのサイズを可変できます.

f:id:tellusium:20180927093915p:plain
Freedomにするとサイズが可変可能

2. 上で作成したxibを持つUIViewのクラスを作成する

xibは見た目のみなので,それを持つUIViewのクラスを作り外部から扱いやすいようにします. 名前はTopView.swiftとしました. xibからViewの情報を取得し,addSubVIewします. この辺りの処理は以下のサイトを参考にさせていただきました.

【iOS】【swift】カスタムViewとxibを紐付ける - tanihiro.log

Swift3 [XIBファイル] コードでの呼び出し方まとめ – RE:ENGINES

xibで作ったCustomViewをStoryboardで使う

import UIKit

@IBDesignable
class TopView: UIView {

    // この2つは後のステップで追加する
    @IBOutlet weak var leftLabel: UILabel!
    @IBOutlet weak var rightLabel: UILabel!
    
    override init(frame: CGRect){
        super.init(frame: frame)
        loadNib()
    }
    
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
        loadNib()
    }
    
    func loadNib(){
        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: "TopView", bundle: bundle)
        let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
        view.frame = self.bounds
        // viewのbackgroundColorはここで設定する必要がある
        view.backgroundColor = UIColor.cyan
        self.addSubview(view)
    }

    // viewの設定
    func setup() {
        self.leftLabel.text = "left"
        self.rightLabel.text = "right"
        self.rightLabel.textColor = UIColor.red
    }
}

3. xibのFile's ownerにCustom Classを指定

先ほど作成したxibファイルのFile's ownerにCustom ClassにTopViewクラスを指定します.

f:id:tellusium:20180930171117p:plain
File's ownerのCustom class

f:id:tellusium:20180930171121p:plain
File's ownerのclass

ViewのCustom classの方に設定するとEXC_BAD_ACCESSで落ちます. この回答を見ると書いてある(よくわかってはいないのですが,型周りの不一致らへんが原因なのかなと考えています…)

ios - Instantiate view from nib throws error - Stack Overflow

f:id:tellusium:20180930171244p:plain
ViewのCustom classを設定するのは🙅‍♀️

4. IBOutletで紐付け

File's ownerのCustom Classの設定が終わるとxibからTopView.swiftの方にIBOutletをつけることが可能になります. 2つのラベルのOutletを設置します.

5. アプリに表示する画面であるViewController,Storyboardを用意する

MainViewControllerという名前でViewControllerを,同様の名前でStoryboardを作成します. Storyboardでは,Viewコンポーネントを配置し,Custom classにTopViewを指定すると,ラベル等々描画されるはず… MainViewControllerにはOutletでViewを紐付け,TopViewsetup()を呼ぶようにします.

class MainViewController: UIViewController {

    @IBOutlet weak var topView: TopView!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        topView.setup()
    }
    ...

f:id:tellusium:20180930174011p:plain
MainViewController.storyboard

結果の画面は↓↓↓

f:id:tellusium:20180930183123p:plain
結果

終わりに

エラーなどを調べているうちに全く同じ方法をしている記事を見つけました. こちらも参考に.

qiita.com

この記事の新規性はあまりないですが,additionalな情報をいくつか加えているのでそれでいいということで…