【iOS開発】frame, bounds, origin, sizeについて

1. はじめに

こんにちは ビンゴ中西です。
今回はiOS開発に欠かせない画面に関する知識について私自身が勉強する過程を載せます。

1.1. UIViewのframeとかboundsとかわかんないよ

frameとかboundsとかなんなのでしょうか。
そういえば、originとかsizeなんてのも出てきている気がする。これって何?

1.2. 参考書籍

iPhoneアプリ開発のコツとツボ35

iPhoneアプリ開発のコツとツボ35

こちらのP64に解説が載っておりましたので、まず導入として勉強させていただくことにします。

2. サンプルソース

参考にさせていただいて書いたソースが以下。
2つのviewを作りどちらも、
frameに x=20, y=100, width=180, hight=200
を初期値に与えています。

後の部分はほとんどNSLog。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];
    self.window.rootViewController = self.viewController;
    
    UIView *redView  = [[UIView alloc] initWithFrame:CGRectMake(20, 100, 180,200)];
    UIView *yellowView = [[UIView alloc] initWithFrame:CGRectMake(20, 100, 180, 200)];
    [redView addSubview:yellowView];
    
    redView.backgroundColor  = [UIColor redColor];
    yellowView.backgroundColor = [UIColor yellowColor];
    
    NSLog(@"redView  : %@", redView);
    NSLog(@"yellowView : %@", yellowView);
    
    NSLog(@"redView.frame  : %@", NSStringFromCGRect(redView.frame));
    NSLog(@"redView.bounds : %@", NSStringFromCGRect(redView.bounds));
    
    NSLog(@"yellowView.frame  : %@", NSStringFromCGRect(yellowView.frame));
    NSLog(@"yellowView.bounds : %@", NSStringFromCGRect(yellowView.bounds));
    
    [self.viewController.view addSubview:redView];
    
    
    [self.window makeKeyAndVisible];
    return YES;
}

2.1 出力(iPhoneシミュレータ)

出力結果:
このようになりました。

2.2 NSLogの結果(見やすいように整形)

NSLogの出力はこんな感じ。見やすいように一部整形しました。

redView  : <UIView: 0x7454ce0; frame = (20 100; 180 200); layer = <CALayer: 0x7454d90>>
yellowView : <UIView: 0x7454ee0; frame = (20 100; 180 200); layer = <CALayer: 0x7454f40>>

redView.frame  : {{20, 100}, {180, 200}}
redView.bounds : {{0, 0}, {180, 200}}

yellowView.frame  : {{20, 100}, {180, 200}}
yellowView.bounds : {{0, 0}, {180, 200}}

frameの値が知りたい時は、NSLogにUIViewをいきなり与えてあげると色んな情報に混じって表示してくれます。ただし、boundsは表示してくれないので、NSStringFromCGRect関数を一度かましてから、出力すると見れます。

3. 出力結果からの考察

結局 frameとboundsって何が違うのということで検索してみると
以下の記事を発見しました。
http://adotout.sakura.ne.jp/?p=525
入れ子にしたUIViewを回転などで変形するとより理解が進みそうです。

3.1 回転させてみよう

それでは、参考URLを元に

yellowView.transform = CGAffineTransformMakeRotation(M_PI * 90 / 180.0f);

を追加して、90度回転させてみましょう。

3.2 出力(iPhoneシミュレータ)

ほとんど正方形なので回転させても若干わかりにくかったのですが、こうなりました。

3.3 NSLogの出力結果

redView.frame  : {{20, 100}, {180, 200}}
redView.bounds : {{0, 0}, {180, 200}}

yellowView.frame  : {{10, 110}, {200, 180}}
yellowView.bounds : {{0, 0}, {180, 200}}

yellowViewのframeががっつり変換されていることがわかりますね。
しかしyellowViewのboundsは回転前と変わっていません。

3.4 frameとboundsのまとめ

以上より、

frameは主に初期値を設定するときに使われる値で、回転などの変換をすると変わる。
親から見た座標を表す。

boundsは自分から見た座標で、回転などの変換をしても変わらない。
ということがわかりました。

4. originとかsizeとかは何?

originとかsizeとかもソースに出てきて気になりますが、こいつらは簡単で、単純に
origin(頂点の座標)とsize(サイズ)を返してくれます。
frameとboundsをNSLogに渡すと実は表示してくれています。

4.1 回転後のソースに以下のNSLogを追加してみました

NSLog(@"redView.frame.size.width  : %f", redView.frame.size.width);
NSLog(@"redView.frame.size.height : %f", redView.frame.size.height);
NSLog(@"redView.frame.origin.x : %f", redView.frame.origin.x);
NSLog(@"redView.frame.origin.y : %f", redView.frame.origin.y);
    
NSLog(@"yellowView.frame.size.width  : %f", yellowView.frame.size.width);
NSLog(@"yellowView.frame.size.height : %f", yellowView.frame.size.height);
NSLog(@"yellowView.frame.origin.x : %f",  yellowView.frame.origin.x);
NSLog(@"yellowView.frame.origin.y : %f",  yellowView.frame.origin.y);

4.2 出力結果(NSLog)

見やすいように整形していますが、以下のようになりました。

redView.frame.size.width  : 180.000000
redView.frame.size.height : 200.000000
redView.frame.origin.x : 20.000000
redView.frame.origin.y : 100.000000

yellowView.frame.size.width  : 200.000000
yellowView.frame.size.height : 180.000000
yellowView.frame.origin.x : 10.000000
yellowView.frame.origin.y : 110.000000

4.3 originとsizeのまとめ

NSLogにframeとかboundsを渡して、origin(座標)とsize(サイズ)を同時に出力することも可能ですが、個別に見たいときには、それぞれ
frame.origin.x, frame.origin.y, frame.size.width, frame.size.hight
bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.hight
でアクセスして出力することができます。
もちろん出力以外にも各種描画計算などで使うことになると思います。

5. 最終形のソースをGithubに上げました

https://github.com/shingo-nakanishi/frameSample

6. 追記

boundsは自分から見た座標とはいうものの
んでは、boundsをいじるとどうなるのでしょうか?

redView.bounds = CGRectMake(20, 0, 180, 200);

回転前のソースに足してみましょう。

こうなりました。
どうやら、自分自身の座標系をいじることで、子供(入れ子にしたView)の位置を変換できるようです。