1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

既にSuperViewを持っているViewオブジェクトを別のSuperViewにaddSubviewしたい!!!!!!!!

Posted at

というときがあります。

問題意識

たとえば、

こんなとき
//高さ30pt、横画面サイズのボタン
let frame = ()
let button = UIButton(frame: frame, width: self.view.frame.size.width, height: 30)
view.addSubview(button)

こんなボタンがあったとします。
このViewは、ユーザーが操作することで、どんどん要素が増えていく仕様です。
(たとえばUITableView)
当初ボタンを配置した場所に追加された要素が達したら、このボタンとカブってしまいます。
選択肢としては、下記3つがあります。

  1. ボタンをView Hierarchyで上の方に配置して、追加された要素の上に表示する
  2. ボタンをView Hierarchyの下の方に配置して、追加された要素の下に表示する
    (つまり隠す)
  3. 要素追加と連動して、位置を変える

選択肢1、2を選ぶのは実装的には楽ですが、あまり許容される状況はないかと思います。
大抵のケースは結局3にするしかないと思います。
そのときに困る(というか困った)のが、
一度ルートviewにaddSubviewしちゃったけど、やっぱこれUITableViewの下にaddSubviewしなおしたいわ
という場合がありました。

試行錯誤

常に再作成

最初に考えたのは、ViewWillAppear()の中で、必ず削除して、再作成するロジックにすることでした。

ViewWillAppear

var button: UIButton?

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    button?.removeFromSuperView()
    //ボタンの作成
}

単純なUIKitのボタンならこれでイケると思いますが、自前で作ったUI部品で、
中に状態持っているようなやつだと、再作成によってその状態がイニシャルされてしまうという問題があります。

そこで表題の
既にSuperViewを持っているViewオブジェクトを別のSuperViewにaddSubviewしたい!!!!!!!!
に至った訳です。

removeFromSuperViewのオプション

removeFromSuperView()した時点で、SuperViewから接続が切れるだけでなく、SubViewのインスタンスの解放もしてしまいます。
解放しないオプションなんてないかな? と思って調べてみましたが、ありませんでした。

オブジェクトを退避できないか?

removeFromSuperView()の前にオブジェクトを退避できないか? というのも考えました。
何を言っているのかというと、

var button: CustomButton?

if let button = button {
    let tempButton = button
    button?.removeFromSuperView()
    targetView.addSubView(tempButton!)
}

これは一見イケそうなのですが、CustomButtonをClass(参照型)として作っていたので、
tempButtonに入れたのは、buttonのアドレスのため、addSubViewすると落ちます。
(試してないですが)
Structだったらワンチャンイケたんでしょうか?

解決法

解決法は簡単でした。
何も考えずにaddSubviewすればいけます。

var button: CustomButton?
var tableView: UITableView?

if (ボタンをルートViewにつけるか/tableViewにつけるかの判定条件) {
    targetView = view
} else {
    targetView = tableView
}

targetView.addSubView(button)

なんで???

そういう仕様でした。

addSubview(_:)

Views can have only one superview. If view already has a superview and that view is not the receiver, this method removes the previous superview before making the receiver its new superview.

(拙訳)Viewは1つしかSuperViewを持てません。もし既にSuperviewを持っていて、かつそのViewが今回のSuperViewとして指定されていないなら、このメソッドは前回のSuperviewを削除してから、指定されたSuperViewを新たなSuperViewとします。

よかったですね。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?