UIView の setFrame: をオーバーライドするときに気をつけるべきこと
UITableViewCellの横幅を調整する — ひよっこ
この辺の記事を読んでいて思ったこと。
こういうミスって、オブジェクト指向に真摯に向き合ってないと、ついついやらかしがちだよなーと思ったので、なるべく分かりやすくメモしてみる。
大前提として、元記事の通り、こんな風にsetFrameがオーバーライドされているとします。
// UITableViewCell のサブクラスにて。 元記事からコピペ - (void)setFrame:(CGRect)frame { frame.origin.x += self.inset; frame.size.width -= 2 * self.inset; [super setFrame:frame]; }
さて、ここでセルが選択されたら少しだけ右に寄って、解除されたら左に戻るようにしてみましょう。
// id<UITableViewDelegate> にて。 UITableViewControllerとかで。 static const CGFloat selectedCellOffset = 10; - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath { if ( ![[tableView indexPathForSelectedRow] isEqual:indexPath] ) { UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; CGRect frame = cell.frame; frame.origin.x += selectedCellOffset; cell.frame = frame; } return indexPath; } - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; CGRect frame = cell.frame; frame.origin.x -= selectedCellOffset; cell.frame = frame; }
実際にやってみなくても分かるようなことですが、
今回のようにframeで取得した値を利用してsetFrameが呼ばれるようなケースでは、先の例はバグを引き起こします。セルがどんどん小さくなっていく。 なんかまぬけで、実行してみるとちょっと笑える(?)
長く説明したけど、そもそも、ゲッタから受け取った値をセッタに代入できない時点で、何かおかしい訳ですよ。 Javaの入門書でも読んだら分かるようなことです。
ベターな解決策を提示してみます、こんな所でしょうか。
簡易版
// UITableViewCellのサブクラスに以下を書き加えましょう - (void)setFrame:(CGRect)frame { frame.origin.x += self.inset; frame.size.width -= 2 * self.inset; [super setFrame:frame]; }
ちょっと凝った版(あんまり変わらないような気も…)
@implementation AKNTableViewCell { CGRect preservedFrame; } - (CGRect)frame { return preservedFrame; } - (void)setFrame:(CGRect)frame { self->preservedFrame = frame; } - (void)layoutSubviews { CGRect insetFrame = [self insetFrame:self->preservedFrame]; [super setFrame:insetFrame]; [super layoutSubviews]; } - (CGRect)insetFrame:(CGRect)frame { frame.origin.x += inset; frame.origin.y += inset; frame.size.width -= 2 * inset; frame.size.height-= 2 * inset; return frame; } @end