【iOS】UI実装ベストプラクティスを考えてみた 〜Storyboardのコンフリクトに悩まされている方々へ〜


初めに

iOSアプリのUI実装をする上で、Storyboardのコンフリクトに悩まされている方は数多いと思います。

普通にStoryboard を使っていると、UIの複数人同時実装なんて、とてもじゃ無いけど出来ないですよね?
だって必ずコンフリクトしますもん。(ひどい時にはファイルを開いただけで、予期せぬ変更が入る)

そんな状況を解決するために、
自分なりのUI実装ベストプラクティスを考えてみました。

さっそく結論

  • UIのデザインは、ViewController(VC)とxibファイルで定義する
  • 遷移はStoryboardで定義する
  • R.swiftを用いて、segueを用いる時のハードコーディングを防ぐ(推奨)

つまり画面はxibとVCで、遷移はStoryboardで定義して、
画面ごとに実装を分割しつつ、アプリ全体のフローを確認しやすくします。

また、segueをソースコードから呼び出すことになるので、R.swiftを用いてハードコーディングを防ぐことをおすすめします。
R.swiftの導入の仕方は、次の記事が参考になります。R.swiftのセットアップ方法(Swift5) - Qiita


こんな利点がある

  1. Storyboardのコンフリクト地獄から解放される
  2. 画面の使い回しがしやすい
    画面と遷移の実装を分けているので、例えば、”同じ画面だけど、遷移が2通りある”なんて時も簡単に対応出来ます。
  3. プロジェクトにViewを使い回す習慣が生まれる(願望)
    この方法はxibファイルを使う事を強制するので、xibファイルを使ったViewの共有がプロジェクトに浸透する(といいなー)

手順

例として次のようなサンプルを作成してみます。

VCとxibファイルの作成

UIのデザインを定義するVCとxibファイルを作成します。

1. ファイルツリーを右クリックでVCを作成

この時、”Also create XIB file”にチェックを入れて、Xibファイルも同時に作成します。

2. xibファイル上でUIパーツを配置

3. UIパーツとVCを紐付け
VCを作成する時に、”Also create XIB file”にチェックを入れてXibファイルを作成していれば、file’s ownerにVCが設定されているので、VCと紐付けできます。
4. VCからxibを読み込み
下記のようにVC側でloadViewをoverrideし、xibファイルを読み込みます。

class FirstViewController: UIViewController {

    override func loadView() {
        // R.swiftを使わない場合
//      if let view = UINib(nibName: “FirstViewController”, bundle: Bundle.main).instantiate(withOwner: self, options: nil).first as? UIView{
//          self.view = view
//      }
        // R.swiftを使う場合
        view = R.nib.firstViewController(owner: self)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

StoryboardとVCを紐付け

  1. Storyboardで画面を作成
  2. Viewを削除
    余分なViewを削除します。 ViewはXibファイルから生成されるため、StoryboardにあるViewは必要ありません。
  3. 画面のCustomClassにVCを指定
    例では”FirstViewController”を指定  

Storyboardで遷移を定義

  1. 普通に遷移を定義
  2. 遷移にidentifierを設定
    VCから遷移を呼び出すために、遷移にidentifierを設定します。

VCから遷移を呼び出し

    // ボタンを押したら次の画面に遷移する
    @IBAction func segueToNextView(_ sender: Any) {
        // R.swiftを使わない場合
        //self.performSegue(withIdentifier: "toSecondView", sender: nil)
        self.performSegue(withIdentifier: R.segue.firstViewController.toSecondView, sender: nil)
    }

動作確認

最後に

最初xibファイルの"file's owner"にVCを指定しておくだけで、viewが読み込まれるものだと思い込み、はまりました。
loadViewでxibファイルの読み込みが必要なんですね。

プロジェクトのメンバーにやり方を浸透させるのは大変だと思いますが、
大勢が参加するプロジェクトほど効果は高いと思います。

参考

StoryBoardの代わりにxibで画面を実装する - Qiita
Swiftxibファイルを呼び出す最も簡単な方法 - Qiita
XIBからViewを生成する4つの実装パターン - Qiita
R.swiftのセットアップ方法(Swift5) - Qiita