AutoLayoutの制約を動的に変更した話
追記 2016/03/26
iOS9.2で意図した通りに動作しなくなったので、
この話の続きを書きました。
— ここまで追記 —
解像度(主に高さ)が異なると、
ToyTone for iOS(App Store)みたいなアプリの場合は、
だいぶ面倒なことになる。
正攻法としては、
いきなりコントロールを配置しないで、
まずは、コンテナとしてテーブルビューのごとくUIView
を配置して、
コンテナの高さの制約は、SuperViewの高さに係数を掛けたものを設定し、
そのコンテナにコントロールを配置するのが一般的と思われる。
今思うと、そうすれば良かったと思うけど、
早く形にしたかったのでコードでなんとかした。
まず、左側のラベルについて、SuperViewの上辺から距離を設定して、
ラベルの右側にあるコントロールの配置は、左側のラベルを基準にするようにした。
次にコードはこんな感じ。
import UIKit class MainContainerView: UIView { var containerHeightAtDesigned: CGFloat = 0.0 var containerHeightAtUpdated: CGFloat = 0.0 var constantsAtDesigned: [UIView : CGFloat] = [:] required init(coder aDecoder: NSCoder) { super.init( coder: aDecoder ) containerHeightAtDesigned = self.frame.height // memo: デザイン時の高さ //println( "MainContainerView.init, frame: \(self.frame)" ) for c in self.constraints() { if let label = c.firstItem as? UILabel { if c.firstAttribute == .Top { constantsAtDesigned[label] = c.constant } } } } override func updateConstraints() { //println( "updateConstraints: \(self.frame)" ) containerHeightAtUpdated = self.frame.height let constraints = self.constraints() for var i = 0; i<constraints.count; i++ { let c = constraints[i] as! NSLayoutConstraint if let label = c.firstItem as? UILabel { if c.firstAttribute == .Top { var val = constantsAtDesigned[label]! val = val * (self.frame.height / containerHeightAtDesigned) //println( "\(label.text) -> \(c.constant) -> \(val)" ) c.constant = val //println() } } } super.updateConstraints() } override func layoutSubviews() { if containerHeightAtUpdated != self.frame.height { //println( "layoutSubviews: \(self.frame)" ) setNeedsUpdateConstraints() } //println( "layoutSubviews: Here!" ) super.layoutSubviews() } }
MainContainerView
というのは、
この右側にあるViewControllerにある最上位のUIView
に設定したクラスである。
これにより、デザイン時の制約を元に、実行時に制約が変更される。
参考にしたページ
UIViewControllerのライフサイクル
http://qiita.com/mo_to_44/items/0ca628b4cc74c8c5599d
めでたし、めでたし。
Leave a Comment