Upload
takemaedenki
View
67
Download
0
Embed Size (px)
Citation preview
iOS レイアウト 制約= Constraints について2016/12/3 TAKEMAEDENKI
1
注意 iOS に関する話なのに家に MAC が無いのでスクショを取れません。 ホワイトボードを使用できれば良いな。
2
前提 Android のような「レイアウト機能を持つコンテナ部品 (LinearLayout) 」などは存在しないのが iOS のザ世界です。 つまり「俺が・・・俺達がレイアウタだ!」ということです。
3
用語 ○View
UI 部品の総称。クラスとしては UIView 。 ○ViewController
いわゆる画面。クラスとしては UIViewController 。 Android で言うと Activity 。 ただ、ページャーやドロワーなど、同一画面内に複数存在可能なので、どちらかというと Fragment に近い。 ○TableView
いわゆるリスト。 Android では ListView 。 レイアウトカスタマイズが可能なものとして、継承元の CollectionView もある。 不可能な分便利になっている面がある。※具体的には高さ計算を軽くする方向の工夫が取り入れられている。 ○ セル= Cell
TableView/CollectionView のアイテム一つ一つ4
用語2 ○Auto Layout
部品自身、もしくは部品と部品の関係性の制約= Constraints の記述によりレイアウト計算を行う仕組み ○ 制約= Constraints
自身のサイズの値、子から親への位置を示すマージン、部品と部品の間のマージンなどを指定可能。 また指定値に対して「その値に一致」「その値以上 ( 要は最小値指定 ) 」「その値以下 ( 要は最大値指定 ) 」が可能。 ○ デリゲート コールバック関数 ○xib/nib ( じぶ / にぶ )
iOS でのレイアウトのファイルの拡張子。 Android ではレイアウトの xml がそれに当たる。 xml と異なり直接の手動編集は無理な感じ。 XCode のインターフェースビルダーという GUI でのみ編集します。 今は xib 。 nib は古い表現。ただし、プログラム側での関連 API には nib の名前が使われたまま。
5
レイアウトを決める要因 A. 自身への力 View 自身の幅、高さを固定する力。表示領域が決まっている写真や解像度が決まっているアイコンなどでよく使う。 絶対値指定が主だが、幅高さ比率 ( アスペクト比 ) 指定も可能。 また、「親の高さに対して 50% 」のような比率指定も可能。 ※例えばフラグ判定によって表示しない部品は「高さの Constraints を 0 にする」などの制御をプログラム側で行う。 ※組み込み系からの視点だと絶対値が多く、 Web 系からの視点だと比率指定が多い気がする。
6
B. 連続する部品に対する力 上から下に向かって並べるなどでのコントロールツリー上で兄弟となる View 間のマージン指定が主。 マージン指定は基本的に絶対値のみ。 もしマージンを相対指定にしたい場合には「自身への固定の力」で示したような 「親 / 他部品の高さに対して 50% 」という高さをもつスペーサとなる View を配置してマージンの代わりとする。 ※要は“位置”に関してはセンタリング以外の相対指定が出来ないということ。 相対指定は ( 今のところ ) 幅高さに関してのみ可能。C. 外側から内側への力 端末の画面サイズは「この中に配置する」という「範囲内に収める」という内向きの力。 ViewController 内やコンテナ内にどう部品を収めるか。 左右上下に対して部品の端を収めれば良いので、イメージしやすく Constraints の指定も簡単。 子から親に対してマージンを設定するのが主。D. 内側から外側への力 TextView の文字列によって幅高さが可変する、画像によって幅高さが可変するなど。 イメージは出来ても実際に設定しようとするとどういう Constraints を指定すればよいか迷うもの。 関係する部品が多くなればなるほど難易度が高くなる。
7
E. TableView/CollectionView の仕様力 なぜ概念でなく具体的な部品なのか? よく使うにもかかわらず最も面倒な力であるためである。 なぜ面倒か? ※以下は縦スクロール前提で TableView における高さを話題にしていきます。 ・ TableView は「すべての子部品=セル の高さを合算しないと " 内部の高さ " が判明しない」 ・ " 内部の高さ " が判明しないと スクロール全体の高さが分からない ・つまり表示範囲外のセルに関してもセル高さ計算デリゲート関数は呼ばれる ・要はセル高さが可変だととても頭が痛くなる 上記条件を真っ当に満たそうとすると以下になる。 ・すべてのセルのレイアウトを行ってその高さを合算する。 ・セル高さが固定なら良いが、画像サイズや情報表示の有無、文字列の長さによって高さ可変する場合には一つ一つレイアウトしないと高さが決まらない。 可変な高さでもセルの数が少なければ良いが、高さ計算が複雑で 1000件以上にもなるとパフォーマンスに影響を及ぼす。
8
○さらに面倒さを増やす仕様力 ・セル生成はデリゲート関数が呼ばれてセルを返す。 ・セル高さはデリゲート関数が呼ばれて Float 値を返す。 ・セル高さ返却デリゲート関数が呼ばれるのはセル生成デリゲート関数の前 ( ゚ д ゚ )
( つ д )⊂ ゴシゴシ (;゚ д ゚ )
( つ д )⊂ ゴシゴシ _, ._ (;゚ Д ゚) …? ! 初めて知ったときには 「あ・・・ありのまま 今起こった事を話すぜ!・・・な、なにを言っているのか分からないと思うが」 と思って髪が高さ方向に可変になりました。 なので
9
TableView を使う前に言っておくッ! おれは今やつの仕様をほんのちょっぴりだが体験した い…いや…体験したというよりはまったく理解を超えていたのだが…… ,. -‐'''''""¨¨¨ ヽ (. __ _,,,... - ァァ フ | あ…ありのまま 今 起こった事を話すぜ! |i i| }! }} / / | |l 、 { j} /,, ィ // | 『おれは奴の前でセルを作っていると i|:! ヾ、 _ / ノ u {:}// 思ったらいつのまにか高さ計算をしていた』ヘ | リ u' } , ノ _,!V, ハ | / ´f ト、 _{ ル {, ィ ' e ラ , 人 な… 何を言ってるのか わからねーとタ思うが /' ヾ | 宀 | {´,) `/ |<⌒ ヽト i ゝ おれも何をされたのかわからなかった… , / ゙ ) ヽ iL レ u' | | ヾl 〉トハ | / _ / ハ ! ニ⊇ ' / :} V::::: ヽ 頭がどうにかなりそうだった… / / 二二二 7'T'' / u' __ /:::::::/ `ヽ /'´r -― 一 Tァ‐゙ ´ '"´ / :::: / -‐ \ 催眠術 (Apple 製品を神とするお布施 ) だとか超スピードだとか / // 广 ¨´ /' / ::::: / ´  ̄`ヽ ⌒ヽ そんなチャチなもんじゃあ 断じてねえ ノ ' / ノ :::::` ー - 、 ___ / ::::: / / ヽ }_ /`丶 /::::::::::::::::::::::::::  ̄ ` ー -{:::... もっと恐ろしいものの片鱗を味わったイぜ…
10
で、話すこと TableView で高さ可変のセルが組み合わさったときの具体的な解法を示したい。
11
高さ可変セル内における Constraints 設定時のポイント
XCode のインターフェースビルダーでの作業です。 ・上から下に向かって " のみ " Constraints を作っていく ・インターフェースビルダー上におけるセル自体の高さは下端が見えないくらい伸ばしてもOK
・セルのルート View( セルにとっての一番の親 ) の下端からのマージンの Constraints 指定は作らない その Constraints は高さ計算によってそれは無意味になるため。 無意味な Constraints 指定を残すことはメンテナンス性を下げる ( 要はあとで見たとき「わけがわからないよ」となる )
・表示する最後の部品より下に、見えない部品として "BottomView" という下端を示すためだけの部品を作っておく12
「セル高さ返却デリゲート関数が呼ばれるのはセル生成デリゲート関数の前」への対応 プログラム側です。 ・セルクラスにセル高さ計算用関数を xib から読み込んで static に格納しておく。 ・セル高さ計算のためだけのセルクラスのインスタンスを static で作って、それをセル高さ計算用関数で利用する ・セル高さ返却デリゲート関数が呼ばれたときにはセルクラスに作った static なセル高さ計算用関数を使用することで、セル生成デリゲート関数より前に高さ計算ができる。
13
セル高さ計算用関数の実装 プログラム側での実装です。 1. (引数で渡される ) セルの幅を設定 2. (引数で渡される )情報に沿って部品の表示 /非表示や文字列の設定などを行う 3. setNeedsLayout() と layoutIfNeeded() の呼び出しで ( 設定幅に従った高さ方向の ) レイアウト処理を行わせる 4. BottomView の Y座標を取得するとセルの高さが取れる
14
わかったけど「セル高さ計算のためだけのセルクラスのインスタンス」をわざわざつくらんくても良くね? xib から読み込んで static で独立したセルインスタンスでなく、 TableView を使ったセル生成関数で使用する dequeueReusableCellWithIdentifier() で作ったセルを使えば良くね? →NG です。 それは以下の TableView の仕様からです。 ・ dequeueReusableCellWithIdentifier() は引数にインデックスを与えます。 ・ TableView はセルを再利用する つまり「再利用されて別のインデックスになっているはずのセル」 に対してセル高さ計算関数の「 2. 情報設定」で中身を変えてしまう可能性があるためです。
15
「高さ計算複雑で 1000件以上にもなるとパフォーマンスに影響を及ぼす」への対応 ・計算した高さの結果を配列にでも覚えよう。 横になったときなどは配列リセットか横用の配列を持つなど。 でも初期表示の重さへの対処にはならない。。。
16
初期表示に関して ・最初は数十件だけ表示して時間差を付けて or スクロール時に TableView へ append する。 reloadData だと最初からの計算が再度始まってしまうので append がオススメ。 ・諦める くるくるを出すなど。 諦めないのなら次に示す estimatedRowHeight や UITableViewAutomaticDimension があります。
17
TableView って通常の高さ =RowHeight だけでなく、推定の高さ= estimatedRowHeight の API も出来たよね? 固定高さなセルの TableView では estimated を使用すべきです。 「表示されない領域のセルに関しては estimated が使われてセル高さ返却デリゲート関数が呼ばれない」 ということが発生して初期表示が軽くなります。 が、高さ可変なセルでは estimated は使うべきではありません。 使った場合「スクロールが飛ぶんだけど!?」が発生し ( てい ) ます。 ※ 今の仕事のアプリで発生している。他社のバグだけど。
18
TableView ってセル高さ自動計算の UITableViewAutomaticDimension の API も出来たよね
標準のセルクラス= UITableViewCell の標準の Text に文字列を入れる場合には OK です。 が、カスタムレイアウトなセルの場合には辞めておいた方が無難です。 解法として示されている「 sizeToFit() オーバーライドで高さを設定する」 を行ったとしても「セルの表示が欠ける」が発生しました。 また、表示欠けが発生しなかったとしても「スクロール時のカクつき」が発生します。 まぁ初期表示があまりにレイアウトで遅いなら、そのトレードオフにはなるかも。
19
その他 ・セル高さ可変で estimatedRowHeight + UITableViewAutomaticDimension を使ってもっとラクに or初期表示を速くできる。という解法があれば教えて下さい。なんでもしますから。 ・ AttributedString はリッチテキスト記載ができる String ですが「画像添付」や「インデント」も使えたりするのでホント便利ですよ。別のラベル /ImageView を作らなくて済みます。 まぁ AttributedString の方がラベルの高さ計算が遅くなりますが。。。 ・ XCode7 の Swift2.2 から XCode8 の Swift3 への変換で苦労をした人は僕と握手! ・ガルパンはいいぞ
20
以上、ご静聴ありがとうございました。21