なんとなく違いは分かっていましたが、いつもframeで事足りていたので、正確には理解していなかったのと、boundsのoriginを変更した時の挙動が分からなかったので調べてみました。
簡単に言ってしまうと、frameとはsuperviewを基点に考えた座標系です。boundsとはローカルviewを起点に考えた座標系になります。
左側の図はviewの左上の原点が(0, 0)の幅150、高さ200のview。 右側の図はviewの左上の原点が(50, 50)で同サイズのview。 viewのframe値に関係なくboundsは自身の座標系を示すので常にoriginは(0, 0)を示していますね。sizeはframeもboundsも同じ値で(150, 200)です。 この例ではboundsのoriginはframeのoriginに影響を受けない事は分かりますが、そこまでしか分かりませんね。実はviewを拡大縮小・回転してみるともっと違いがはっきりとして来ます。以下で回転した場合の例を示します。
viewを回転したり、拡大縮小した場合はどうなるのでしょうか。これでなんとなく違いが見えてくると思います。
下の図は左側にあるsubviewを右回転したものが右側の図になります。subviewには分かりやすい様に画像を貼り付けています。
frameの値はアファイン変換(回転、拡大縮小)により本来のframe値ではなく、変換後のviewを囲う様な値で再定義されてしまっています(青枠)。ですので、もう描画時の座標計算には使えないでしょう。この事は、「iOS view プログラミングガイド」にこう記されています。
transformした場合はframe値を使わずに、boundsやcenterプロパティ値を使用して、viewサイズや中心点を求める必要があるようですね。回転してもboundsプロパティ値の方は変化無しです。ローカルviewを起点に考えているのでsuperviewがどのように変化しても値に変化が現れないのは理解出来ます。
以下に、viewを1回転した場合のアニメーションgifを載せています。viewの回転角度によってframeが刻々と変化する様子が分かると思います。
上記のアプリはGitHubで公開していますので、宜しかったらダウンロードして実際に動かしてみてください。レポジトリは ViewFrameBoundsIndicatorで公開しています。
さて、boundsのoriginプロパティ値は、デフォルト値で常に(0,0)を指しています。この値を変更するとどうなるのでしょうか。
実はsubviewを作成しただけではこの違いに気付くことが出来ません。subviewの中に更にsubviewを作成して初めて違いが見えてきます。 図では、subviewの中にUIImageViewをsubviewとしてaddしています。因みに画像はお寿司ですよ。
それではboundsのoriginを変更してみましょう。確認用のアプリからboundsのoriginを変更してみます。
わかります?boundsのoriginを変更するとsubviewの中がスクロールしているかのように見えます。 もっと分かりやすくするためにsubviewのclipToBoundsをoffにしてみます。因みに緑枠がboundsの矩形で、青枠がfraneの矩形です。
これでイメージがついたのではないでしょうか。subviewのbounds originを変更する事で、そのsubviewのsubviewとの座標系をずらすことが出来ます。offset値を設定しているイメージでしょうか。以下に静止画のイメージも載せておきます。subviewのsubview座標をずらして表示していることがイメージ出来ますでしょうか。
GitHubにも上げていますが、ViewControllerのソースを貼り付けておきます。
GitHub URL: https://github.com/takuran/ViewFrameBoundsIndicator
- import UIKit
- class ViewController: UIViewController {
- @IBOutlet weak var boundXSlider: UISlider!
- @IBOutlet weak var boundYSlider: UISlider!
- @IBOutlet weak var rotationSlider: UISlider!
- @IBOutlet weak var detailTextView: UITextView!
- var targetView: UIView!
- var targetSubView: UIImageView!
- var frameLayer: CALayer!
- var boundsLayer: CALayer!
- @IBOutlet weak var clipBoundsSwitch: UISwitch!
- override func viewDidLoad() {
- super.viewDidLoad()
- targetView = UIView(frame: CGRect(x: 0, y: 0, width: 150, height: 200))
- targetView.center = CGPoint(x: view.frame.width / 2, y: 200)
- targetView.backgroundColor = UIColor.whiteColor()
- targetView.layer.contentsScale = UIScreen.mainScreen().scale
- targetView.clipsToBounds = true
- // sub sub view
- targetSubView = UIImageView(frame: CGRect(x: 0, y: 0, width: 150, height: 200))
- targetSubView.image = UIImage(named: "C789_unitoikuramaguro_TP_V.jpg")
- targetSubView.contentMode = UIViewContentMode.TopLeft
- targetSubView.layer.contentsScale = UIScreen.mainScreen().scale
- // add to sub view
- targetView.addSubview(targetSubView)
- // add to superview
- // view.addSubview(targetView)
- view.insertSubview(targetView, atIndex: 0)
- // layer of indicating frame border line
- frameLayer = CALayer()
- frameLayer.frame = targetView.frame
- frameLayer.backgroundColor = UIColor.clearColor().CGColor
- frameLayer.borderColor = UIColor.blueColor().CGColor
- frameLayer.borderWidth = 2.0
- view.layer.addSublayer(frameLayer)
- // layer of indicating bounds border line
- boundsLayer = CALayer()
- boundsLayer.frame = targetView.bounds
- boundsLayer.backgroundColor = UIColor.clearColor().CGColor
- boundsLayer.borderColor = UIColor.greenColor().CGColor
- boundsLayer.borderWidth = 2.0
- targetView.layer.addSublayer(boundsLayer)
- // information
- detailTextView.text = "frame: \(targetView.frame)\nbounds: \(targetView.bounds)\ncenter: \(targetView.center)"
- print("targetView frame : \(targetView.frame)")
- print("targetView bounds: \(targetView.bounds)")
- print("targetView center: \(targetView.center)")
- // slider handler settings
- boundXSlider.addTarget(self, action: #selector(changeBoundsHandler), forControlEvents: UIControlEvents.ValueChanged)
- boundYSlider.addTarget(self, action: #selector(changeBoundsHandler), forControlEvents: UIControlEvents.ValueChanged)
- rotationSlider.addTarget(self, action: #selector(changeBoundsHandler), forControlEvents: UIControlEvents.ValueChanged)
- }
- override func didReceiveMemoryWarning() {
- super.didReceiveMemoryWarning()
- // Dispose of any resources that can be recreated.
- }
- func changeBoundsHandler(sender: AnyObject) {
- if boundXSlider == sender as! NSObject {
- // x
- targetView.bounds.origin.x = (targetSubView.image!.size.width - targetView.bounds.width) * CGFloat(boundXSlider.value) / 2
- } else if boundYSlider == sender as! NSObject {
- // y
- targetView.bounds.origin.y = (targetSubView.image!.size.height - targetView.bounds.height) * CGFloat(boundYSlider.value) / 2
- } else if rotationSlider == sender as! NSObject {
- // rotation
- let transform = CGAffineTransformMakeRotation(2 * CGFloat(M_PI) * CGFloat(rotationSlider.value))
- targetView.transform = transform
- }
- // update layer of frame
- frameLayer.frame = targetView.frame
- boundsLayer.frame = targetView.bounds
- // information
- detailTextView.text = "frame: \(targetView.frame)\nbounds: \(targetView.bounds)\ncenter: \(targetView.center)"
- }
- @IBAction func changeSwitchHandler(sender: AnyObject) {
- targetView.clipsToBounds = clipBoundsSwitch.on
- }
- }
参考URL http://stackoverflow.com/questions/1210047/cocoa-whats-the-difference-between-the-frame-and-the-bounds/28917673#28917673
0 件のコメント :
コメントを投稿