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