UIView の frame を Blocks を活用して変更する

昨日、なんで今までこれをやってなかったんだろう?と思ったこと。

frame 弄るのダルい

UIView の layout 時に

label.frame.origin.y = 5;

とかしたいところですけど出来ません。 この程度の事で

label.frame = CGRectMake(CGRectGetMinX(label.frame),
                         5,
                         CGRectGetWidth(label.frame),
                         CGRectGetHeight(label.frame));

とかはダルすぎるので、一回変数に受けておいてから

CGRect frame = label.frame;
frame.origin.y = 5;
label.frame = frame;

くらいが妥当なところでしょうか。

これだと以下のような問題によく陥ります。

  • 複数のViewのレイアウトを行う場合に一時変数名に困る
  • 変更した rect を View に突っ込むのをうっかり忘れる

Blocks 使えば良かったんだ

この問題に対して以下のようなカテゴリメソッドを定義して

- (void)cs_changeFrameWithBlock:(CGRect (^)(CGRect frame))block
{
    if (block) {
        self.frame = block(self.frame);
    }
}

こんな感じでやれば良かったわけです。

[label cs_changeFrameWithBlock:^CGRect (CGRect frame) {
    frame.origin.y = 5;
    return frame;
}];

こうすることで

  • Block のスコープに縛られるので他の一時変数と被ることはない
  • 外の情報には問題なくアクセスできる
  • CGRect を return し忘れるとビルドに失敗するのですぐ気づく
  • ある View に対する frame の変更のコードブロック(塊)がわかりやすくて見やすくなる

まとめ

すでにそんなことやってるよとかもっといい方法を実践してるよっていう感じでしょうが、こんなくだらない小ネタでも気付きになるかと思って書きました。以前にも Blocks を使ったちょっとした便利な使い方を書きました。これからも何か気付きがあれば書いてみようと思います。