UIView
とそのサブクラスはすべてプロパティframe
andを持っていますbounds
。違いは何ですか?
UIView
とそのサブクラスはすべてプロパティframe
andを持っていますbounds
。違いは何ですか?
回答:
境界のUIViewのは、ある矩形独自の座標系(0,0)からの相対位置(x、y)及びサイズ(幅、高さ)として表されます。
UIViewのフレームは長方形であり、その中に含まれているスーパービューを基準とした位置(x、y)とサイズ(width、height)で表されます。
したがって、スーパービューの25,25(x、y)に配置された100x100(幅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でした)。
UIViewをカバーする素晴らしいプレゼンテーションもあります。フレームと境界の違いを説明するだけでなく、視覚的な例も示すスライド1〜20を参照してください。
frame = 親ビューの座標系を使用したビューの位置とサイズ
bounds = 独自の座標系を使用したビューの位置とサイズ
私が覚えて支援するためのフレームを、私は考える壁に額縁。額縁は、ビューの境界線のようなものです。私は壁のどこにでも絵を掛けることができます。同様に、親ビュー(スーパービューとも呼ばれる)内の任意の場所にビューを配置できます。親ビューは壁のようなものです。iOSの座標系の原点は左上です。ビューフレームのxy座標を(0、0)に設定することで、ビューをスーパービューの原点に配置できます。これは、壁の左上隅に画像をぶら下げるようなものです。右に移動するにはxを増やし、下に移動するにはyを増やします。
境界を思い出すのを助けるために、私は時々バスケットボールが境界からノックアウトされるバスケットボールコートを考えます。あなたはバスケットボールコート全体にボールをドリブルしていますが、コート自体がどこにあるかは気にしていません。ジムや高校の外、家の前などです。それは問題ではありません。あなたはバスケットボールをしたいだけです。同様に、ビューの境界の座標系は、ビュー自体のみを考慮します。ビューが親ビューのどこにあるかについては何も知りません。境界の原点(デフォルトではポイント(0、0))は、ビューの左上隅です。このビューにあるサブビューは、このポイントに関連してレイアウトされます。それはバスケットボールをコートの左前隅に持っていくようなものです。
フレームと境界を比較しようとすると、混乱が生じます。しかし、実際は最初のように悪くはありません。理解を助けるためにいくつかの写真を使用しましょう。
左側の最初の画像には、親ビューの左上にあるビューがあります。黄色の長方形はビューのフレームを表します。右側には再びビューが表示されますが、今回は親ビューは表示されていません。これは、境界が親ビューを認識していないためです。緑の長方形はビューの境界を表します。両方の画像の赤い点は、フレームまたは境界の原点を表しています。
Frame
origin = (0, 0)
width = 80
height = 130
Bounds
origin = (0, 0)
width = 80
height = 130
したがって、フレームと境界はその画像でまったく同じでした。それらが異なる例を見てみましょう。
Frame
origin = (40, 60) // That is, x=40 and y=60
width = 80
height = 130
Bounds
origin = (0, 0)
width = 80
height = 130
したがって、フレームのxy座標を変更すると、親ビュー内でフレームが移動することがわかります。ただし、ビュー自体の内容はまったく同じに見えます。境界は何かが違うということを知りません。
これまでは、フレームと境界の幅と高さはまったく同じでした。ただし、それは常に正しいとは限りません。ビューを時計回りに20度回転するとどうなるか見てください。(回転は変換を使用して行われます。詳細については、ドキュメント、およびこれらのビューとレイヤーの例を参照してください。)
Frame
origin = (20, 52) // These are just rough estimates.
width = 118
height = 187
Bounds
origin = (0, 0)
width = 80
height = 130
境界がまだ同じであることがわかります。彼らはまだ何かが起こったことを知りません!ただし、フレーム値はすべて変更されています。
これで、フレームと境界の違いが少しわかりやすくなりましたね。おそらくフレームと境界を理解していないという記事では、ビューフレームを次のように定義しています。
...そのビューに適用されるすべての変換を含む、その親の座標系に関するそのビューの最小の境界ボックス。
ビューを変換すると、フレームが未定義になることに注意することが重要です。したがって、実際には、上の画像の回転した緑の境界線の周りに描いた黄色のフレームは実際には存在しません。つまり、回転、拡大縮小、またはその他の変換を行う場合は、フレーム値を使用しないでください。ただし、境界値は引き続き使用できます。アップルのドキュメントは警告します:
重要:ビューの
transform
プロパティに恒等変換が含まれていない場合、そのビューのフレームは未定義であり、自動サイズ変更動作の結果も同様です。
むしろ、自動サイズ変更については残念ですが...できることはあります。
transform
ビューのプロパティを変更すると、すべての変換はビューの中心点を基準にして実行されます。
そのため、変換が完了した後で親内でビューを移動する必要がある場合は、view.center
座標を変更することで実行できます。同様にframe
、center
親ビューの座標系を使用しています。
さて、回転を取り除き、境界に焦点を合わせましょう。これまでのところ、境界の原点は常に(0、0)のままです。ただし、その必要はありません。ビューに、一度に表示するには大きすぎる大きなサブビューがある場合はどうなりますか?UIImageView
大きな画像で作ります。これも上から2番目の画像ですが、今回はビューのサブビューのコンテンツ全体がどのようになるかを確認できます。
Frame
origin = (40, 60)
width = 80
height = 130
Bounds
origin = (0, 0)
width = 80
height = 130
画像の左上隅のみがビューの境界内に収まります。境界の原点座標を変更するとどうなるか見てみましょう。
Frame
origin = (40, 60)
width = 80
height = 130
Bounds
origin = (280, 70)
width = 80
height = 130
フレームはスーパービューで移動していませんが、境界の長方形の原点がビューの別の部分から始まっているため、フレーム内のコンテンツが変更されています。これは、a UIScrollView
とそのサブクラス(a など)の全体的な考え方UITableView
です。詳細については、UIScrollViewについてを参照してください。
frame
は親ビュー内のビューの位置を関連付けるため、幅を変更したり、ビューと親ビューの上部との間の距離を見つけたりするなど、外向きの変更を行うときに使用します。
描画やビュー内のサブビューの配置など、内側の変更bounds
を行うときにを使用します。ビューに変換を行った場合は、境界を使用してビューのサイズを取得します。
Appleドキュメント
関連するStackOverflowの質問
その他の資料
上記の記事を読むことに加えて、テストアプリを作成するのに非常に役立ちます。同様のことを試してみてください。(このビデオコースからアイデアを得ましたが、残念ながら無料ではありません。)
あなたの参照のためのコードはここにあります:
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))"
}
}
以下のコードを実行してみてください
- (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}}
明らかに、フレームと境界の違いを見ることができます
フレームは、スーパービューの座標系でのビューの原点(左上隅)とサイズです。つまり、フレームの原点を変更することにより、スーパービューでビューを移動します。一方、境界は、そのサイズと原点です。独自の座標系なので、デフォルトでは境界の原点は(0,0)です。
ほとんどの場合、フレームと境界は合同ですが、たとえば、フレーム((140,65)、(200,250))と境界((0,0)、(200,250))のビューがあり、ビューが傾斜している場合右下隅に立つように、境界は((0,0)、(200,250))のままですが、フレームはそうではありません。
フレームはビューをカプセル化/囲む最小の長方形になるため、(写真のように)フレームは((140,65)、(320,320))になります。
別の違いは、たとえば、境界が((0,0)、(200,200))のスーパービューがあり、このスーパービューにフレームが((20,20)、(100,100))のサブビューがあり、スーパービューの境界を変更した場合です。 ((20,20)、(200,200))に変更すると、subViewフレームは((20,20)、(100,100))のままですが、(20,20)によってオフセットされます。これは、そのスーパービュー座標系が(20、 20)。
これが誰かの役に立つことを願っています。
subView
フレームはそれに関連していsuperView
ます。superView
境界を何かに変更しても、subView
原点はそれと一致しませんsuperView
。
5セント追加します。
フレームは、ビューの親ビューが親ビュー内に配置するために使用されます。
Boundsは、ビュー自体が自身のコンテンツを配置するために使用されます(スクロールビューがスクロール中に行うように)。clipsToBoundsも参照してください。境界は、ビューのコンテンツを拡大/縮小するためにも使用できます。
類推:
フレーム〜TV画面の
境界〜カメラ(ズーム、移動、回転)
上記の回答は、境界とフレームの違いを非常によく説明しています。
境界:独自の座標系によるビューのサイズと位置。
フレーム:SuperViewを基準にしたビューサイズと位置。
次に、境界の場合、X、Yが常に「0」になるという混乱があります。これは真実ではありません。これは、UIScrollViewとUICollectionViewでも理解できます。
境界のx、yが0でない
場合。UIScrollViewがあると仮定します。ページネーションを実装しました。UIScrollViewには3つのページがあり、そのContentSizeの幅は画面の幅の3倍です(ScreenWidthが320であると想定)。高さは一定です(200と想定)。
scrollView.contentSize = CGSize(x:320*3, y : 200)
3つのUIImageViewをサブビューとして追加し、フレームの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)
ページ0:ScrollViewが0ページの場合、境界は (x:0、y:0、幅:320、高さ:200)になります。
ページ1:スクロールしてページ1に移動します。
境界は(x:320、y:0、幅:320、高さ:200)になり
ます。独自の座標系を基準にして言ったことを思い出してください。これで、ScrollViewの「Visible Part」の320が「x」になります。imageView1のフレームを見てください。
UICollectionViewの場合も同様です。collectionViewを見る最も簡単な方法は、それをスクロールして、その境界を印刷/ログに記録することです。
フレームとバインド
frame =親ビューの座標系を使用したビューの位置とサイズ
bounds =独自の座標系を使用したビューの位置とサイズ
ビューは、フレーム長方形と境界長方形の2つの長方形を使用して、サイズと位置を追跡します。フレームの長方形は、スーパービューの座標系を使用して、スーパービュー内のビューの位置とサイズを定義します。境界長方形は、原点とスケーリングを含むビューのコンテンツを描画するときに使用される内部座標系を定義します。図2-1は、左側のフレームの長方形と右側の境界の長方形の関係を示しています。」
つまり、フレームはスーパービューのビューのアイデアであり、境界はビュー自体のアイデアです。ビューごとに1つずつ、複数の座標系を持つことは、ビュー階層の一部です。