web-dev-qa-db-ja.com

ココア:フレームと境界の違いは何ですか?

UIViewとそのサブクラスはすべて、プロパティframeboundsを持ちます。違いは何ですか?

549
mk12

UIView範囲は、 長方形 、それ自体の座標系(0,0)を基準とした位置(x、y)とサイズ(width、height)として表されます。

UIView長方形 、含まれるスーパービューに対する位置(x、y)およびサイズ(幅、高さ)として表されます。

したがって、スーパービューの25,25(x、y)に配置された100 x 100(幅x高さ)のサイズのビューを想像してください。次のコードは、このビューの境界とフレームを表示します。

// This method is in the view controller of the superview
- (void)viewDidLoad {
    [super viewDidLoad];

    NSLog(@"bounds.Origin.x: %f", label.bounds.Origin.x);
    NSLog(@"bounds.Origin.y: %f", label.bounds.Origin.y);
    NSLog(@"bounds.size.width: %f", label.bounds.size.width);
    NSLog(@"bounds.size.height: %f", label.bounds.size.height);

    NSLog(@"frame.Origin.x: %f", label.frame.Origin.x);
    NSLog(@"frame.Origin.y: %f", label.frame.Origin.y);
    NSLog(@"frame.size.width: %f", label.frame.size.width);
    NSLog(@"frame.size.height: %f", label.frame.size.height);
}

そして、このコードの出力は次のとおりです。

bounds.Origin.x: 0
bounds.Origin.y: 0
bounds.size.width: 100
bounds.size.height: 100

frame.Origin.x: 25
frame.Origin.y: 25
frame.size.width: 100
frame.size.height: 100

したがって、どちらの場合も、境界とフレームのどちらを見ているかに関係なく、ビューの幅と高さは同じです。違いはビューのx、y位置です。境界の場合、x座標とy座標は0,0です。これらの座標はビュー自体に対するものです。ただし、フレームのx座標とy座標は、親ビュー内でのビューの位置を基準にしています(以前に25,25になっていました)。

UIViewsをカバーする 素晴らしいプレゼンテーション もあります。スライド1〜20を参照してください。これはフレームと境界の違いを説明するだけでなく、視覚的な例も示しています。

859
shek

短い答え

frame = 親ビューの座標系を使用したビューの位置とサイズ

  • 重要:ビューを親に配置する

bounds = 独自の座標系を使用したビューの位置とサイズ

  • 重要:ビューのコンテンツまたはサブビューをその中に配置する

詳細な回答

frameを思い出すために、私は壁の額縁を思い浮かべます。額縁は、ビューの境界のようなものです。私は壁のどこにでも写真を掛けることができます。同様に、親ビュー(スーパービューとも呼ばれます)内の任意の場所にビューを配置できます。親ビューは壁のようなものです。 iOSの座標系の原点は左上です。ビューフレームのx-y座標を(0、0)に設定することで、スーパービューの原点にビューを配置できます。これは、壁の左上隅に写真を掛けるようなものです。右に移動するにはxを増やし、下に移動するにはyを増やします。

バウンドを覚えやすくするために、バスケットボールコートを思い浮かべます。時々バスケットボールが範囲外にノックアウトされる。あなたはバスケットボールコート全体にボールをドリブルしていますが、コート自体がどこにあるかはあまり気にしません。ジム、屋外の高校、家の前などにあります。関係ありません。あなたはただバスケットボールをしたいだけです。同様に、ビューの境界の座標系は、ビュー自体のみを考慮します。ビューが親ビューのどこにあるかについては何も知りません。境界の原点(デフォルトではポイント(0、0))は、ビューの左上隅です。このビューに含まれるサブビューはすべて、この点に関連して配置されます。バスケットボールをコートの左前に持っていくようなものです。

フレームと境界を比較しようとすると、混乱が生じます。しかし、実際には最初に見えるほど悪くはありません。理解を助けるために、いくつかの写真を使用しましょう。

フレームと境界

左側の最初の図では、親ビューの左上にあるビューがあります。 黄色の長方形はビューのフレームを表します。右側に再びビューが表示されますが、今回は親ビューは表示されません。これは、境界が親ビューについて知らないためです。 緑の長方形はビューの境界を表します。両方の画像の赤点は、フレームまたは境界のを表します。

Frame
    Origin = (0, 0)
    width = 80
    height = 130

Bounds 
    Origin = (0, 0)
    width = 80
    height = 130

enter image description here

そのため、フレームと境界はその写真ではまったく同じでした。それらが異なる例を見てみましょう。

Frame
    Origin = (40, 60)  // That is, x=40 and y=60
    width = 80
    height = 130

Bounds 
    Origin = (0, 0)
    width = 80
    height = 130

enter image description here

したがって、フレームのx-y座標を変更すると、親ビューでフレームが移動することがわかります。ただし、ビュー自体のコンテンツはまったく同じように見えます。境界には、何かが違うという考えがありません。

これまで、フレームと境界の両方の幅と高さはまったく同じでした。ただし、常にそうとは限りません。ビューを時計回りに20度回転するとどうなるか見てみましょう。 (回転は変換を使用して行われます。詳細については、 ドキュメント およびこれらの view および レイヤーの例 を参照してください。)

Frame
    Origin = (20, 52)  // These are just rough estimates.
    width = 118
    height = 187

Bounds 
    Origin = (0, 0)
    width = 80
    height = 130

enter image description here

境界はまだ同じであることがわかります。彼らはまだ何も起きていないことを知らない!ただし、フレーム値はすべて変更されています。

フレームと境界の違いを見るのが少し簡単になりましたね。記事 おそらくフレームと境界を理解していない は、ビューフレームを

...そのビューに適用される変換を含む、その親の座標系に関するそのビューの最小の境界ボックス。

ビューを変換すると、フレームが未定義になることに注意することが重要です。したがって、実際には、上の画像の回転した緑の境界の周りに描いた黄色のフレームは実際には存在しません。つまり、回転、拡大縮小、またはその他の変換を行う場合、フレーム値を使用しないでください。ただし、境界値は引き続き使用できます。 Appleドキュメントは警告します:

重要:ビューのtransformプロパティに恒等変換が含まれていない場合、そのビューのフレームは未定義であり、自動サイズ変更動作の結果も同様です。

自動サイズ変更については残念ながら…。でも、できることはあります。

Appleドキュメントの状態:

ビューのtransformプロパティを変更すると、すべての変換はビューの中心点を基準にして実行されます。

したがって、変換が完了した後に親内でビューを移動する必要がある場合は、view.center座標を変更することでそれを実行できます。 frameと同様に、centerは親ビューの座標系を使用します。

さて、回転を取り除き、境界に注目しましょう。これまで、Originは常に(0、0)のままでした。ただし、その必要はありません。ビューに、一度に表示するには大きすぎる大きなサブビューがある場合はどうなりますか?大きな画像を含むUIImageViewにします。再び上から2番目の画像を示しますが、今回は、ビューのサブビューのコンテンツ全体がどのようになるかを確認できます。

Frame
    Origin = (40, 60)
    width = 80
    height = 130

Bounds 
    Origin = (0, 0)
    width = 80
    height = 130

enter image description here

ビューの境界内に収まるのは、画像の左上隅のみです。境界のOrigin座標を変更するとどうなるか見てみましょう。

Frame
    Origin = (40, 60)
    width = 80
    height = 130

Bounds 
    Origin = (280, 70)
    width = 80
    height = 130

enter image description here

スーパービューではフレームは移動していませんが、境界長方形の原点はビューの別の部分から始まるため、フレーム内のコンテンツは変更されています。これは、UIScrollViewとそのサブクラス(たとえば、UITableView)の背後にある考え方全体です。詳細については、 IScrollViewを理解する を参照してください。

フレームを使用する場合と境界を使用する場合

frameは親ビューのビューの位置に関連するため、outward changesを作成するときに使用します。たとえば、幅を変更したり、ビューと親ビューの上部の距離を見つけたりします。 。

内向きの変更を行うときは、boundsを使用します。たとえば、ビュー内に物を描画したり、サブビューを配置したりします。また、変換を行った場合は、境界を使用してビューのサイズを取得します。

さらなる研究のための記事:

Apple docs

関連するStackOverflowの質問

その他のリソース

練習する

上記の記事を読むことに加えて、テストアプリを作成するのに非常に役立ちます。同様のことをしようとするかもしれません。 ( このビデオコース からアイデアを得ましたが、残念ながら無料ではありません。)

enter image description here

参照用のコードは次のとおりです。

import UIKit

class ViewController: UIViewController {


    @IBOutlet weak var myView: UIView!

    // Labels
    @IBOutlet weak var frameX: UILabel!
    @IBOutlet weak var frameY: UILabel!
    @IBOutlet weak var frameWidth: UILabel!
    @IBOutlet weak var frameHeight: UILabel!
    @IBOutlet weak var boundsX: UILabel!
    @IBOutlet weak var boundsY: UILabel!
    @IBOutlet weak var boundsWidth: UILabel!
    @IBOutlet weak var boundsHeight: UILabel!
    @IBOutlet weak var centerX: UILabel!
    @IBOutlet weak var centerY: UILabel!
    @IBOutlet weak var rotation: UILabel!

    // Sliders
    @IBOutlet weak var frameXSlider: UISlider!
    @IBOutlet weak var frameYSlider: UISlider!
    @IBOutlet weak var frameWidthSlider: UISlider!
    @IBOutlet weak var frameHeightSlider: UISlider!
    @IBOutlet weak var boundsXSlider: UISlider!
    @IBOutlet weak var boundsYSlider: UISlider!
    @IBOutlet weak var boundsWidthSlider: UISlider!
    @IBOutlet weak var boundsHeightSlider: UISlider!
    @IBOutlet weak var centerXSlider: UISlider!
    @IBOutlet weak var centerYSlider: UISlider!
    @IBOutlet weak var rotationSlider: UISlider!

    // Slider actions
    @IBAction func frameXSliderChanged(sender: AnyObject) {
        myView.frame.Origin.x = CGFloat(frameXSlider.value)
        updateLabels()
    }
    @IBAction func frameYSliderChanged(sender: AnyObject) {
        myView.frame.Origin.y = CGFloat(frameYSlider.value)
        updateLabels()
    }
    @IBAction func frameWidthSliderChanged(sender: AnyObject) {
        myView.frame.size.width = CGFloat(frameWidthSlider.value)
        updateLabels()
    }
    @IBAction func frameHeightSliderChanged(sender: AnyObject) {
        myView.frame.size.height = CGFloat(frameHeightSlider.value)
        updateLabels()
    }
    @IBAction func boundsXSliderChanged(sender: AnyObject) {
        myView.bounds.Origin.x = CGFloat(boundsXSlider.value)
        updateLabels()
    }
    @IBAction func boundsYSliderChanged(sender: AnyObject) {
        myView.bounds.Origin.y = CGFloat(boundsYSlider.value)
        updateLabels()
    }
    @IBAction func boundsWidthSliderChanged(sender: AnyObject) {
        myView.bounds.size.width = CGFloat(boundsWidthSlider.value)
        updateLabels()
    }
    @IBAction func boundsHeightSliderChanged(sender: AnyObject) {
        myView.bounds.size.height = CGFloat(boundsHeightSlider.value)
        updateLabels()
    }
    @IBAction func centerXSliderChanged(sender: AnyObject) {
        myView.center.x = CGFloat(centerXSlider.value)
        updateLabels()
    }
    @IBAction func centerYSliderChanged(sender: AnyObject) {
        myView.center.y = CGFloat(centerYSlider.value)
        updateLabels()
    }
    @IBAction func rotationSliderChanged(sender: AnyObject) {
        let rotation = CGAffineTransform(rotationAngle: CGFloat(rotationSlider.value))
        myView.transform = rotation
        updateLabels()
    }

    private func updateLabels() {

        frameX.text = "frame x = \(Int(myView.frame.Origin.x))"
        frameY.text = "frame y = \(Int(myView.frame.Origin.y))"
        frameWidth.text = "frame width = \(Int(myView.frame.width))"
        frameHeight.text = "frame height = \(Int(myView.frame.height))"
        boundsX.text = "bounds x = \(Int(myView.bounds.Origin.x))"
        boundsY.text = "bounds y = \(Int(myView.bounds.Origin.y))"
        boundsWidth.text = "bounds width = \(Int(myView.bounds.width))"
        boundsHeight.text = "bounds height = \(Int(myView.bounds.height))"
        centerX.text = "center x = \(Int(myView.center.x))"
        centerY.text = "center y = \(Int(myView.center.y))"
        rotation.text = "rotation = \((rotationSlider.value))"

    }

}
602
Suragch

以下のコードを実行してみてください

- (void)viewDidLoad {
    [super viewDidLoad];
    UIWindow *w = [[UIApplication sharedApplication] keyWindow];
    UIView *v = [w.subviews objectAtIndex:0];

    NSLog(@"%@", NSStringFromCGRect(v.frame));
    NSLog(@"%@", NSStringFromCGRect(v.bounds));
}

このコードの出力は次のとおりです。

ケースデバイスの向きが縦

{{0, 0}, {768, 1024}}
{{0, 0}, {768, 1024}}

ケースデバイスの向きが横

{{0, 0}, {768, 1024}}
{{0, 0}, {1024, 768}}

明らかに、あなたはフレームと境界の違いを見ることができます

43
tristan

フレームは、UIViewをそのスーパービューに関してで定義する長方形です。

境界四角形は、NSViewの座標系を定義する値の範囲です。

つまり、この四角形の中にあるものはすべてUIViewに実際に表示されます。

13
jorik

frameは、スーパービューの座標系におけるビューの原点(左上隅)とサイズです。つまり、スーパービューでビューを変換するということです。フレームのOriginを変更すると、boundsはサイズとOriginの座標系になります。したがって、デフォルトでは、Originの境界は(0、 0).

ほとんどの場合、フレームと境界は一致していますが、たとえばフレームのビュー((140,65)、(200,250))と境界((0,0)、(200,250))があり、ビューが傾いている場合右下隅に立つようにしても、境界は((0,0)、(200,250))のままですが、枠はそうではありません。

enter image description here

フレームはビューをカプセル化/囲む最小の長方形になるので、(写真のように)フレームは((140,65)、(320,320))になります。

もう1つの違いは、たとえば、境界が((0,0)、(200,200))のsuperViewがあり、このsuperViewのフレームが((20,20)、(100,100))のsubViewで、superViewの境界を変更した場合です。 ((20,20)、(200,200))にすると、subViewフレームは((20,20)、(100,100))静止した状態になりますが、そのスーパービュー座標系は(20、20)オフセットされているため20)。

これが誰かに役立つことを願っています。

9
m.eldehairy

そのSuperViewを基準にしてフレームを作成し、NSViewを基準にして境界を設定します。

例:X = 40、Y =60。3つのビューも含まれています。この図は、明確なアイデアを示しています。

FRAME

BOUNDS

4
RAGHUNATH

上記の答えは、BoundsとFramesの違いを非常によく説明しています。

境界:独自の座標系によるビューのサイズと位置。

フレーム:スーパービューに対するビューのサイズと位置。

それで、境界のXの場合、Yは常に "0"になるという混乱があります。 これは正しくありません。これは、UIScrollViewとUICollectionViewでも理解できます。

範囲x、yが0でない場合
UIScrollViewがあるとしましょう。ページネーションを実装しました。 UIScrollViewは3ページあり、そのContentSizeの幅はScreen Widthの3倍です(ScreenWidthが320であると仮定します)。高さは一定です(200とします)。

scrollView.contentSize = CGSize(x:320*3, y : 200)

SubViewとして3つのUIImageViewを追加して、frameのxの値をよく見てください

let imageView0 = UIImageView.init(frame: CGRect(x:0, y: 0 , width : scrollView.frame.size.width, height : scrollView.frame.size.height))
let imageView1 :  UIImageView.init( frame: CGRect(x:320, y: 0 , width : scrollView.frame.size.width, height : scrollView.frame.size.height))
let imageView2 :  UIImageView.init(frame: CGRect(x:640, y: 0 , width : scrollView.frame.size.width, height : scrollView.frame.size.height))
scrollView.addSubview(imageView0)
scrollView.addSubview(imageView0)
scrollView.addSubview(imageView0)
  1. ページ0:ScrollViewが0ページのとき、境界は(x:0、y:0、幅:320、高さ:200)になります

  2. 1ページ目:スクロールして1ページ目に移動します。
    境界は(x:320、y:0、幅:320、高さ:200)になります独自の座標系に。そのため、ScrollViewの "Visible Part"の "x"は320になります。imageView1のフレームを見てください。

  3. ページ2:スクロールしてページ2に移動する範囲:(x:640、y:0、幅:320、高さ:200)もう一度見てください。 imageView2のフレームで

UICollectionViewの場合も同じです。 collectionViewを見る最も簡単な方法は、それをスクロールしてその境界を印刷/ログに記録することです。

1
Muhammad Nayab

フレーム=親ビューの座標系を使用したビューの位置とサイズ

bounds =独自の座標系を使用したビューの位置とサイズ

ビューは、フレーム長方形と境界長方形の2つの長方形を使用して、サイズと位置を追跡します。フレームの長方形は、スーパービューの座標系を使用して、スーパービュー内のビューの位置とサイズを定義します。境界の四角形は、Originやスケーリングなど、ビューのコンテンツを描画するときに使用される内部座標系を定義します。図2-1は、左側のフレーム長方形と右側の境界長方形の関係を示しています。

要するに、フレームはビューのスーパービューのアイデアであり、境界はビューの独自のアイデアです。ビューごとに1つずつ、複数の座標系を持つことは、ビュー階層の一部です。

0
Xab Ion

上記のすべての答えは正しいです、そしてこれは私の考えです。

フレームと境界を区別するにはCONCEPTS開発者は読むべきです:

  1. スーパービュー(1つの親ビュー)に対して、= FRAME内に含まれています。
  2. 自身の座標系を基準にして、そのサブビュー位置を決定します。= BOUNDS

「境界」は、座標がそれが設定されているビューの位置であるという印象を与えるので、混乱を招きます。しかし、これらは関係があり、フレーム定数に従って調整されます。

iPhone screen

0
Marina