【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](https://qiita.com/uhooi/items/82fbdd94bdc467a22422)

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407360/c5a778c9-47ba-d890-54fa-4f641cae78d9.png" width=100%>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407360/4d6a4f40-6086-b8a6-4bd2-e5f0e1b4dd77.png" width=30%>


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

## 手順
例として次のようなサンプルを作成してみます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407360/72f0a1a8-9081-acde-abb0-77beec4f5579.gif" width=30%>

### VCとxibファイルの作成
UIのデザインを定義するVCとxibファイルを作成します。  
1. **ファイルツリーを右クリックでVCを作成**
![スクリーンショット 2019-10-20 22.07.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407360/ba20db8f-4593-66ef-d24a-32e40f90056e.png)
この時、”Also create XIB file”にチェックを入れて、Xibファイルも同時に作成します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407360/c17c865e-325a-dc62-1452-2eac6a2b47a8.png" width=70%>
2. **xibファイル上でUIパーツを配置**
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407360/5be6ad5a-94dc-0562-14f5-25b27e95210e.png" width=30%>
3. **UIパーツとVCを紐付け**
VCを作成する時に、”Also create XIB file”にチェックを入れてXibファイルを作成していれば、file’s ownerにVCが設定されているので、VCと紐付けできます。
4. **VCからxibを読み込み**
下記のようにVC側でloadViewをoverrideし、xibファイルを読み込みます。

```swift
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で画面を作成**  
![スクリーンショット 2019-10-21 0.02.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407360/a85df513-020d-3b44-da44-1058056103fd.png)
2. **Viewを削除**  
余分なViewを削除します。
ViewはXibファイルから生成されるため、StoryboardにあるViewは必要ありません。   
![スクリーンショット 2019-10-21 0.15.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407360/f0e61273-ebeb-01f6-766c-fa1b0ee545ec.png)
4. **画面のCustomClassにVCを指定**  
例では”FirstViewController”を指定　　
![スクリーンショット 2019-10-21 0.16.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407360/ad13538d-79ca-73d5-c361-9b6b6fcc1e23.png)



### Storyboardで遷移を定義
1. **普通に遷移を定義**  
![スクリーンショット 2019-10-21 0.22.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407360/085b40ce-929e-a773-e005-c2488e83ce64.png)
2. **遷移にidentifierを設定**  
VCから遷移を呼び出すために、遷移にidentifierを設定します。
![スクリーンショット 2019-10-21 0.22.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407360/e71473a1-8025-32e4-aff3-c0ab5a1c7e5d.png)

### VCから遷移を呼び出し

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

## 動作確認
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407360/72f0a1a8-9081-acde-abb0-77beec4f5579.gif" width=30%>

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

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

## 参考
[StoryBoardの代わりにxibで画面を実装する - Qiita](https://qiita.com/kawano108/items/e66b0559960b22f15297)
[Swiftxibファイルを呼び出す最も簡単な方法 - Qiita](https://qiita.com/kichiemon/items/3cfa6c2bf2a0acb299a0)
[XIBからViewを生成する4つの実装パターン - Qiita](https://qiita.com/marty-suzuki/items/7c7ecdcb1f16c21b0051)
[R.swiftのセットアップ方法(Swift5) - Qiita](https://qiita.com/uhooi/items/82fbdd94bdc467a22422)