iPhoneX対応：UnityでNGUIのAnchorをセーフエリア分ずらす

#環境
Unity 4.7.2f1
NGUI 3.7.8

#はじめに
今月からiPhoneX対応始めました。（残り1月無いよ...）
▽Qiita - iPhone Xのセーフエリアやマージン幅について
https://qiita.com/usagimaru/items/761e9a5f3d78b1939df8

セーフエリアの対応にてNGUIのAnchorをずらす作業を行ったのでまとめておきます。
セーフエリアの取得方法はここでは触れません。

#先にまとめ
``` C#
		// Topのセーフエリア対応を無視するか
		[SerializeField] bool m_IsIgnoreTop;

		// Bottomのセーフエリア対応を無視するか
		[SerializeField] bool m_IsIgnoreBottom;

		void Awake()
		{
			// UIAnchorを使っている場合（relative offset考慮してない）
			UIAnchor anchor = gameObject.GetComponent<UIAnchor>();
			if(anchor != null){
				SetAnchorOffset (anchor);
				return;
			}

			// UIRectを使っている場合
			UIRect rect = gameObject.GetComponent<UIRect>();
			if (rect != null) {
				SetAnchorAbsolute (rect);
				return;
			}
		}

		/// <summary>
		/// anchorのoffsetを設定する
		/// （UIAnchorは非推奨（
		/// </summary>
		void SetAnchorOffset(UIAnchor anchor){
			ScreenUtility.EdgeInsets edge = ScreenUtility.GetSafeAreaUI();

			UIAnchor.Side side = anchor.side;
			Vector2 offset = anchor.pixelOffset;

			switch (side) {
			case UIAnchor.Side.Bottom:
			case UIAnchor.Side.BottomLeft:
			case UIAnchor.Side.BottomRight:
				if (!m_IsIgnoreBottom) {
					anchor.pixelOffset.Set(offset.x, offset.y + edge.bottom);
				}
				break;
			case UIAnchor.Side.Top:
			case UIAnchor.Side.TopLeft:
			case UIAnchor.Side.TopRight:
				if (!m_IsIgnoreTop) {
					anchor.pixelOffset.Set(offset.x, offset.y - edge.top);
				}
				break;
			}
		}

		/// <summary>
		/// anchorのabsoluteを設定する
		/// </summary>
		/// <param name="rect">Rect.</param>
		void SetAnchorAbsolute(UIRect rect){
			ScreenUtility.EdgeInsets edge = ScreenUtility.GetSafeAreaUI();

			// top基準:1 center基準:0.5 bottom基準:0
			float topRelative = rect.topAnchor.relative;
			float bottomRelative = rect.bottomAnchor.relative;

			float topMovingValue = 0f;
			float bottomMovingValue = 0f;

			if (topRelative == 1f && bottomRelative == 1f) {
				topMovingValue -= edge.top;
				bottomMovingValue -= edge.top;
			} else if (topRelative == 0f && bottomRelative == 0f){
				topMovingValue += edge.bottom;
				bottomMovingValue += edge.bottom;
			}else if(topRelative == 1f && bottomRelative == 0f){
				topMovingValue -= edge.top;
				bottomMovingValue += edge.bottom;
			}

			if (!m_IsIgnoreTop) {
				rect.topAnchor.absolute += (int)topMovingValue;
			}
			if (!m_IsIgnoreBottom) {
				rect.bottomAnchor.absolute += (int)bottomMovingValue;
			}
		}
```
これをセーフエリア対応したいAnchorがattachされているGameObjectにattachします。

#解説
##legacyの「UIAnchor」の対応

▽NGUIリファレンス - UIAnchor
http://tasharen.com/ngui/docs/class_u_i_anchor.html
![スクリーンショット 2018-06-10 14.45.32.png](https://qiita-image-store.s3.amazonaws.com/0/44990/de56656d-175a-f5f5-de3f-a5d5e44992f6.png)
legacyなのでこれ以上使わないようにと警告が表示されます。
置き換えたいところですが工数がエライことになるのでこれはそっとしておく...。
このクラスはenum SideでAnchorをつける位置を指定します。
SideがBottom系であればセーフエリアのBottom分底上げし、SideがTop系であればセーフエリアのTop分を下にずらす処理を入れることで対応しました。


##「UIRect」の対応
▽NGUIリファレンス - UIRect
http://tasharen.com/ngui/docs/class_u_i_rect.html
![スクリーンショット 2018-06-10 14.48.25.png](https://qiita-image-store.s3.amazonaws.com/0/44990/c57842fc-2d78-9c8f-2a34-26e27e840128.png)
UIWidgetだけではなく、UIRectから派生しているクラスであれば基底であるUIRectをGetComponentすることができます。

``` C#
	// UIRectを使っている場合
	UIRect rect = gameObject.GetComponent<UIRect>();
	if (rect != null) {
		SetAnchorAbsolute (rect);
		return;
	}
```
UIRectには「topAnchor」「bottomAnchor」「leftAnchor」「rightAnchor」の4つのAnchorPointがあります。
今回は「topAnchor」「bottomAnchor」を使います。
UIRect の Anchor は relative の値によってAnchorの位置を指定します。

指定したアンカーの位置とrelativeの関係性は以下の通りでした。
・Target's Top を設定していれば relative の値は 1f
・Target's Center を設定していれば relative の値は 0.5f
・Target's Bottom を設定していれば relative の値は 0f

それぞれのAnchorPointにTarget's Bottomを指定していればセーフエリアのBottom分底上げし、Target's Topを指定していればセーフエリアのTop分を下にずらす処理を入れることで対応しました。
また、TopのAnchorPointにTarget's Topしている、かつBottomのAnchorPointにTarget's Bottomを設定していれば領域が狭まるように対応を入れました。


#さいごに
あとはこれをattachしていくだけ...と思いきや、
・コードで位置ずらしている
・legacyとそうでないAnchorが混在している
・親オブジェクトAnchorと子オブジェクトのAnchorが別のTarget見ている
などなど、一筋縄ではいかないもので...