読者です 読者をやめる 読者になる 読者になる

ひとりまとめ

もろもろのメモ

Dynamic Typeとか使いつつTableViewのCellの高さを変更したい!しかもカスタマイズなセルで!

「なかなか上達しないシリーズUITableVIew編」みたいなことになってますが、今回は、いろんな要素組み合わせまくりです。

 

まずやりたいこと。結構あるパターンなんじゃないかなと。

・TableViewのセルをカスタマイズしたい

・セルに表示するテキストの文量によって、セルの高さを変えたい

※できるだけコーディングせず、Storyboardで!

 

やりたかったことそのものではないのですが、iOS7時代ともなると文字の指定の仕方が変わってきて、Dynamic Typeという方式が登場しています。文字サイズなどを個別に指定するのではなく、スタイルで指定するらしい。利点は、システムで文字サイズを変更すると、それに合わせてアプリ内の文字も変更できちゃう。せっかくなのでこれも利用してみます。

 

大まかな流れを整理すると、次のようになります。

カスタマイズしたセルをxibでつくる

UITableViewCellのサブクラスをつくり、セルの高さ計算のメソッドを追加

TableViewControllerでtableViewで下準備

セルの高さを返すdelegateを追加

セルを表示

 

カスタマイズしたセルをxibで作る

1:File→New→File...で、空のUserInterfaceを1つ作ります。

f:id:g2_girichan:20140331005415p:plain

 

2:xibを開いて、Table View Cellを配置し、テキストを表示するラベルを置きます。

もちろん他の要素があってもかまいませんが、備忘録なので最小要素。このラベルをDynamic Typeで設定するために、ラベルのAttributes inspectorのFont欄にある「T」を押して、Text Stylesから任意のスタイルを選択します。このスタイルは、後でラベルの立て幅を計算するときにも使うので覚えておいてください。

また、同じくこのラベルのLinesを0にします。0とは無制限という意味なので、文字が入った分だけずるずる下に伸びていきます。

f:id:g2_girichan:20140331011833p:plain

 

さらに、AutoLayoutで上下左右のスペースを指定します。こうすることで、余白の長さを保ったまま、ラベルの立て幅が伸びた分だけTableViewCellの縦の長さも伸びていきます。

f:id:g2_girichan:20140331010830p:plain

 

 

UITableViewCellのサブクラスをつくり、セルの高さ計算のメソッドを追加 

3:UITableViewCellのサブクラスを作る。

File→New→File...で、UITableViewCellのサブクラスを1つ作ります。これを先ほど作ったxibのカスタムクラスに指定します。

f:id:g2_girichan:20140331012503p:plain

紐づけた後、ラベルなど必要な要素をoutletで繋いでおきます。

 

4:UITableViewCellのサブクラスにセルの高さを計算するメソッドを作る

必ずしもここに作る必要はないのですが、「cellのことはcellに聞け!」の教え(?)にしたがってこのクラスに記述して外から呼べるようにします。

ラベルの高さを求めるには、表示するテキスト(NSString)のboundingRectWithSize:options:attributes:context:というメソッドで、引数には次の要素を使います。

・ラベルの横幅 ←AutoLayoutで設定した左右の幅

・ラベルのスタイル ←ラベルのAttributes inspectorのFont

 

具体的な設定方法は、次のようにしてみました。

ラベルの横幅は「Viewの横幅」−「左右の余白」なのでこう。

CGRect myBounds = self.bounds; // viewの横幅を取り出すつもり

CGFloat PADDNG_LR = 20 * 2; // 余白が左右20ずつだったので

CGFloat labelWidth = myBounds.size.width - PADDNG_LR;

 

ラベルのスタイルも定義しておきます

UIFont *labelFont = [UIFont preferredFontForTextStyle:UIFontTextStyleBody]; 

 

でもって、こいつを実行。testStringはラベルに表示したい文字列のことです。このメソッドで引数として受け取るようにしました。

CGRect totalRect = [testString boundingRectWithSize:CGSizeMake(labelWidth, CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) attributes:@{NSFontAttributeName:labelFont} context:nil]; 

CGFLOAT_MAXは縦幅はながーくという意味で,optionsは計算する際の基準。このあたりの値はNSAttributedString UIKit Additionsに書かれてました。

 

このtotalRect.size.heightで、その文字を表示するために必要な立て幅が求まります。注意は、totalRect.size.heightが小数点があるってこと。そのまま使うと微妙に立て幅が足りないって事態がでるようなので、ceilf(totalRect.size.height)などとして整数にします。

これは可変なラベル1つ分の縦幅なので必要に応じてラベルを足し算し、さらに上下や要素間の隙間の値などを足してあげます。合計の縦幅をreturnするメソッドにすれば、UITableViewCellのサブクラスは終了。

※せっかくAutoLayoutで設定してるのでoutletの要素を参照して値を参照したいんですけど、カスタムクラスをインスタンスにしただけではダメっぽげ。nib読み込みとかすればいいのかもだけど、ぐるぐる読み込みが回ったりしそうで・・・ということで断念。レイアウトを変更するたびにここも調整しないといけないのであんまりスマートじゃなさげです。いい方法を知りたい。

 

TableViewControllerでtableViewで下準備

5:TableViewにセルを登録

まず、UITableViewCellのサブクラスをimportします。続いて、作ったcellをtableViewで使うための下ごしらえをします。これはviewDidLoadに記述します。

UINib *nib = [UINib nibWithNibName:@"CustomCell" bundle:nil];

[self.tableView registerNib:nib forCellReuseIdentifier:@"cell_a"];

作ったセルをnibとしておきます。CustomCellはxibの名前です。そのnibをregisterNib:forCellReuseIdentifierでcell_aという名前で使えるように登録しました。この名前は、セルを実際に作るときに使います。

 

セルの高さを返すdelegateを追加

6:– tableView:heightForRowAtIndexPath:を追加して処理を書く

UITableViewControllerを新規で作ってもこのメソッドはないので、新たに追加します。もしも固定で広げたいならreturnで固定の値を返してもいいです。たとえxibで作ったセルの高さが可変でなく固定でも、ただセルを表示するようにしただけではデフォルトの高さにしかなってくれないので注意です。

で、今回はセルに表示するテキストによってセルの高さが変わるので、このメソッドの中でつどつど計算します。「4」で作ったメソッドの出番です。UITableViewCellのサブクラスのインスタンスを作り、このメソッドに表示したい文字列を渡して、セルの高さの計算結果を受け取ります。

※このあとセルを作るときに表示したいテキストを使うので、なんとかひとまとめにできないものかと思ったりします。しかし、呼ばれる順番的に高さ計算→セルを生成であったりとかいろいろあって、今のところこの方法なのかなと。

 

セルを表示

7:– tableView:cellForRowAtIndexPath: でセルを作る

最初に忘れちゃダメなのは、このセルをまとめるTableViewControllerにCellの為に作ったクラスをimportしておくこと。ついうっかりそのままやると、当たり前ですがせっかく作った機能が使えません。あぶねー。

さて、件のメソッドはいつものメソッドですね。ただし自分で作ったセルを表示するところが異なります。新規にUITableViewControllerのサブクラスを作ったらこんな風に書かれてます。

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:<@"reuseIdentifier"> forIndexPath:indexPath];

この「reuseIdentifier」というところに、viewDidLoadで決めたセルの名前cell_aを指定します。なお、この方法でxibのセルを作った場合は、nil判定でなかったら作るっていう処理を省略して良いらしいです。

 

あとはセルのラベルにテキストを設定してあげるだけ。return cellでそのセルを返しちゃえば終了です。

 

 

ちょっとセルの高さを変えたいだけなのに、iOSのバージョンによって変わったことだとかxibを作ってみたりだとか、別のことがいろいろあってややこしく感じてしまいました。なんとかできたのでメモ保存。