概要
動的にUIView
に別のUIView
を追加したい場合には、addSubview
というメソッドが利用できます。
//view1にview2を追加したい場合
var view1 = UIView(frame:CGRectMake(0, 0, 200, 200))
view1.backgroundColor = UIColor.blueColor()
var view2 = UIView(frame:CGRectMake(50, 50, 100, 100))
view2.backgroundColor = UIColor.greenColor()
view1.addSubview(view2)
では、view2
をview1
以外のビューに対してaddSubview
を使って追加したらどうなるのでしょうか??
//view1, view1Aにview2を追加したい場合
var view1 = UIView(frame:CGRectMake(0, 0, 200, 200))
view1.backgroundColor = UIColor.blueColor()
var view1A = UIView(frame:CGRectMake(0, 300, 200, 200))
view1A.backgroundColor = UIColor.redColor()
var view2 = UIView(frame:CGRectMake(50, 50, 100, 100))
view2.backgroundColor = UIColor.greenColor()
view1.addSubview(view2)
view1A.addSubview(view2)
最後に追加したview1Aにのみ追加されるようです。
ということは、view1
に追加されていたview2
はremoveFromSuperview
によって削除されたのでしょうか?
view1にもview1Aにも追加されると思ってしまうケースも多いような気がします
検証
Case1: 追加先を順々に変更してみる
見た目で挙動を確認するために次のようなコードを書いてみました。
let averageHeight = CGRectGetHeight(UIScreen.mainScreen().bounds) / 11.0
let averageWidth = CGRectGetWidth(UIScreen.mainScreen().bounds) / 2.0
//5つviewを追加するためのviewを用意する
var subview1 = UIView(frame: CGRectMake(averageHeight,
averageHeight,
averageHeight,
averageHeight))
subview1.backgroundColor = UIColor.blueColor()
subview1.center = CGPointMake(averageWidth, subview1.center.y)
var subview2 = UIView(frame: CGRectMake(averageHeight,
averageHeight*3,
averageHeight,
averageHeight))
subview2.backgroundColor = UIColor.greenColor()
subview2.center = CGPointMake(averageWidth, subview2.center.y)
var subview3 = UIView(frame: CGRectMake(averageHeight,
averageHeight*5,
averageHeight,
averageHeight))
subview3.backgroundColor = UIColor.redColor()
subview3.center = CGPointMake(averageWidth, subview3.center.y)
var subview4 = UIView(frame: CGRectMake(averageHeight,
averageHeight*7,
averageHeight,
averageHeight))
subview4.backgroundColor = UIColor.yellowColor()
subview4.center = CGPointMake(averageWidth, subview4.center.y)
var subview5 = UIView(frame: CGRectMake(averageHeight,
averageHeight*9,
averageHeight,
averageHeight))
subview5.backgroundColor = UIColor.blackColor()
subview5.center = CGPointMake(averageWidth, subview5.center.y)
//追加するビューを用意する
targetView = UIView(frame: CGRectMake(averageHeight/4, averageHeight/4, averageHeight/2, averageHeight/2))
targetView.layer.cornerRadius = 4.0
targetView.backgroundColor = UIColor.purpleColor()
NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: "moveTargetView:", userInfo: nil, repeats: true)
viewList = [subview1, subview2, subview3, subview4, subview5]
subview1.addSubview(targetView)
func moveTargetView(timer:NSTimer) {
//追加先ビューの変更
currentIndex = (currentIndex >= viewList.count - 1) ? 0 : currentIndex + 1
let view = viewList[currentIndex]
view.addSubview(targetView)
}
moveTargetView:
がcallされる度にビューの位置が変わっていますね。
追加される先は1つのように見えます。
Case2: RetainCountの確認
以下の様なコードを書いて追加されたchildView
のRetainCountを計測してみました。
UIView *parentView = [[UIView alloc] init];
UIView *childView = [[UIView alloc] init];
NSLog(@"Number of RetainCount = %ld", (long)CFGetRetainCount((__bridge CFTypeRef)childView));
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
NSLog(@"Number of RetainCount = %ld", (long)CFGetRetainCount((__bridge CFTypeRef)childView));
2015-07-18 13:00:03.168 test-objC[9989:328391] Number of RetainCount = 1
2015-07-18 13:00:03.170 test-objC[9989:328391] Number of RetainCount = 2
childView
のRetainCountはインスタンス生成 & parentView
へのaddSubview
の2回分しかないみたいです。
念のため、ParentView
のsubviews
の数も確認してみました。
UIView *parentView = [[UIView alloc] init];
UIView *childView = [[UIView alloc] init];
NSLog(@"Number of Subviews = %lu", (unsigned long)parentView.subviews.count);
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
[parentView addSubview:childView];
NSLog(@"Number of Subviews = %lu", (unsigned long)parentView.subviews.count);
2015-07-18 13:03:16.407 test-objC[10052:330128] Number of Subviews = 0
2015-07-18 13:03:16.408 test-objC[10052:330128] Number of Subviews = 1
Case3: addSubviewをする先を変更した場合に[will|did]MoveToSuperviewを確認
例えば、view1にview2を追加した場合やview2をview1から削除した場合、view2に対して,
willMoveToSuperview(newSuperview: UIView?)
、didmoveToSuperView()
が呼ばれます。
willMoveToSuperview
の第一引数がnil
の場合は、superviewから削除された場合です。 確認するため、以下のように
View2`クラスのそれぞれのメソッドでログを吐くようにしてみました
var view1 = UIView(frame:CGRectMake(0, 0, 200, 200))
view1.backgroundColor = UIColor.blueColor()
var view1A = UIView(frame:CGRectMake(0, 300, 200, 200))
view1A.backgroundColor = UIColor.redColor()
var view2 = View2(frame:CGRectMake(50, 50, 100, 100))
view2.backgroundColor = UIColor.greenColor()
view1.addSubview(view2) //View2クラスのview2にview1を追加
view1A.addSubview(view2) //View2クラスのview2にview1Aを追加
class View2: UIView {
override func willMoveToSuperview(newSuperview: UIView?) {
let isExistSuperview = (newSuperview != nil) ? true : false
println("willMoveToSuperview: \(isExistSuperview)")
}
override func didMoveToSuperview() {
println("didMoveToSuperview")
}
}
willMoveToSuperview: true
didMoveToSuperview
willMoveToSuperview: true
didMoveToSuperview
あれ。。。?! 呼ばれてないというかここは通らないよう...。
明示的にview1からview1Aに追加先を変更する前に、removeFromSuperview
を呼ぶと、やはりそれぞれ3回ずつ呼ばれました。
willMoveToSuperview: true
didMoveToSuperview
willMoveToSuperview: false
didMoveToSuperview
willMoveToSuperview: true
didMoveToSuperview
まとめ
- 同じ
UIView
クラスのオブジェクトを別のUIView
クラスのオブジェクトに追加した場合、直前の追加先からは削除され、参照カウンタに変動はない - 暗黙的に
removeFromSuperview
が呼ばれているような挙動だが、willMoveToSuperview
などは呼ばれない
ということでした