CGRectDivide のすすめ

前回も座標系だったんですけど今回も座標系。

CGGeometry にはいろいろ地味に便利な関数が揃ってるんですけどあまり日の目を見ない気がするので CGRectDivide について書こうかと思います。

使い方と得られる結果が理解しにくい

CGRectDivide は任意の Rect を指定した方向から切り取り2つの Rect に分割してくれる関数です。 定義は以下の通り。

void CGRectDivide (
   CGRect rect,
   CGRect *slice,
   CGRect *remainder,
   CGFloat amount,
   CGRectEdge edge
);

実行するとこんな感じ。

CGFloat amount = 44.0f;
CGRect baseRect = self.view.frame;
CGRect sliceRect     = CGRectNull;
CGRect remainderRect = CGRectNull;

CGRectDivide(baseRect, &sliceRect, &remainderRect, amount, CGRectMinYEdge);
NSLog(@"%s \nbaseRect:      %@\nsliceRect:     %@\nremainderRect: %@", __PRETTY_FUNCTION__, NSStringFromCGRect(baseRect), NSStringFromCGRect(sliceRect), NSStringFromCGRect(remainderRect));

この例の場合、

  • sliceRect には baseRect の画面上部(CGRectMinYEdge)から 44 pt(amount) の高さで切り取った rect
  • remainderRect には baseRect からさっきの sliceRect を取り除いた rect

が入ります。

まとめると

baseRect を CGrectEdge で指定した方向から amount 分を切り取った rect が sliceRect に。
そのあまりが remainderRect に入れられる。

ということになります。

これでも、私の物忘れのヒドさでは使い方を忘れてしまいそうなのでこんな関数群を定義して使っています。

UIKIT_STATIC_INLINE void CSCGRectVerticalDivideFromTop(CGRect baseRect, CGFloat amountFromTop, void(^resultBlock)(CGRect topRect, CGRect bottomRect)) {
    CGRect sliceRect     = CGRectNull; /// this would be top rectangle.
    CGRect remainderRect = CGRectNull; /// this would be bottom rectangle.
    
    /**
     baseRect を CGrectEdge で指定した方向から amount 分を切り取った rect が sliceRect に。
     そのあまりが remainderRect に入れられる。
     */
    
    CGRectDivide(baseRect, &sliceRect, &remainderRect, amountFromTop, CGRectMinYEdge);

    if (resultBlock) {
        resultBlock(sliceRect, remainderRect);
    }
}

UIKIT_STATIC_INLINE void CSCGRectVerticalDivideFromBottom(CGRect baseRect, CGFloat amountFromBottom, void(^resultBlock)(CGRect topRect, CGRect bottomRect)) {
    CGRect topRect = CGRectNull;
    CGRect bottomRect = CGRectNull;
    CGRectDivide(baseRect, &bottomRect, &topRect, amountFromBottom, CGRectMaxYEdge);
    //NSLog(@"%s \n%@\n%@\n%@", __PRETTY_FUNCTION__, NSStringFromCGRect(baseRect), NSStringFromCGRect(topRect), NSStringFromCGRect(bottomRect));
    if (resultBlock) {
        resultBlock(topRect, bottomRect);
    }
}

UIKIT_STATIC_INLINE void CSCGRectHorizontalDivideFromLeft(CGRect baseRect, CGFloat amountFromLeft, void(^resultBlock)(CGRect leftRect, CGRect rightRect)) {
    CGRect sliceRect     = CGRectNull; /// this would be left rectangle.
    CGRect remainderRect = CGRectNull; /// this would be right rectangle.
    
    /**
     baseRect を CGrectEdge で指定した方向から amount 分を切り取った rect が sliceRect に。
     そのあまりが remainderRect に入れられる。
     */
    
    CGRectDivide(baseRect, &sliceRect, &remainderRect, amountFromLeft, CGRectMinXEdge);

    if (resultBlock) {
        resultBlock(sliceRect, remainderRect);
    }
}

UIKIT_STATIC_INLINE void CSCGRectHorizontalDivideFromRight(CGRect baseRect, CGFloat amountFromRight, void(^resultBlock)(CGRect leftRect, CGRect rightRect)) {
    CGRect sliceRect     = CGRectNull; /// this would be left rectangle.
    CGRect remainderRect = CGRectNull; /// this would be right rectangle.
    
    /**
     baseRect を CGrectEdge で指定した方向から amount 分を切り取った rect が sliceRect に。
     そのあまりが remainderRect に入れられる。
     */
    
    CGRectDivide(baseRect, &sliceRect, &remainderRect, amountFromRight, CGRectMaxXEdge);
    if (resultBlock) {
        resultBlock(remainderRect, sliceRect);
    }
}

どっちから(上から/下から/左から/右から)amount 分を切り取るかを別関数として定義して Blocks で切り取った結果を返す感じです。これなら CGRectDivide の使い方を忘れても大丈夫。

使用例

UIViewController の view に UILabel を貼ったものです。

コード

CSCGRectVerticalDivideFromTop(self.view.bounds, 44, ^(CGRect topRect, CGRect bottomRect) {
    self.topLabel.frame = topRect;
    self.bottomLabel.frame = bottomRect;
});

結果

f:id:griffin-stewie:20130521165750p:plain


コード

CSCGRectHorizontalDivideFromRight(self.view.bounds, 100, ^(CGRect leftRect, CGRect rightRect) {
    self.leftLabel.frame = leftRect;
    self.rightLabel.frame = rightRect;
});

結果

f:id:griffin-stewie:20130521165753p:plain

使いどころ

結構いっぱいあると思います。

  • UITableViewCell なんかで左側に画像、右側には文字情報群を配置したい
  • ViewController の View の上端ないし下端にはバー的な UI を置いて残りの部分がメインコンテンツ領域という時の配置

オマケ

上下分割して、広い方をさらに
上下分割して、広い方をさらに
左右分割して、広い方をさらに
左右分割とかあえてやってみた。

コード

CSCGRectVerticalDivideFromTop(self.view.bounds, 44, ^(CGRect topRect, CGRect bottomRect) {
    self.topLabel.frame = topRect;
    CSCGRectVerticalDivideFromBottom(bottomRect, 44, ^(CGRect topRect, CGRect bottomRect) {
        self.bottomLabel.frame = bottomRect;
        CSCGRectHorizontalDivideFromLeft(topRect, 44, ^(CGRect leftRect, CGRect rightRect) {
            self.leftLabel.frame = leftRect;
            CSCGRectHorizontalDivideFromRight(rightRect, 44, ^(CGRect leftRect, CGRect rightRect) {
                self.rightLabel.frame = rightRect;
            });
        });
    });
});

結果

f:id:griffin-stewie:20130521165746p:plain