Storyboardで画面のレイアウトやUI部品をグラフィカルに配置できますが、チームで協同作業する時や細かいレイアウトの調整が発生する場合はStoryboardだとどうしても支障が出ます。
autolayoutの標準APIについて、AppleがNSLayoutConstraint
とNSLayoutAnchor
(ios9~)、Visual Format Language(VFL)
を提供しています。NSLayoutAnchor
はNSLayoutConstraint
よりコード量が少なくなり、制約・声明もはっきりするようになっています。VFL
はアスキーアートのように制約を定義することができる記法ですが、学習コストが高くかつリテラルな箇所が多いため、実行時にエラーが起こる恐れがあります(本文ではVFL
パターンを紹介しません)。
1、NSLayoutConstraint
NSLayoutConstraintを使用するには、各々初期化する必要があります。
制約する属性ごとにNSLayoutConstraint
を作る必要があるため、コード量が増えます。
初期化メソッド:NSLayoutConstraint.init()
//UIImageViewを作成し、viewに追加する
let layoutIcon = "layoutIcon"
let layoutImg = UIImage(named: layoutIcon)
let layoutView = UIImageView(image: layoutImg)
layoutView.backgroundColor = .green
layoutView.layer.cornerRadius = 10
//AutoresizingMaskをAutoLayoutの制約に置き換えるかどうか指定する値(必ずfalse)
layoutView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(layoutView)
//制約を追加 top:50
//パラメーター説明 item:制約オブジェクト attribute:制約する属性 relatedBy:制約タイプ toItem:制約の相手 attribute:制約相手に使用する属性 multiplier:乗数値 constant:定数値
let topConstraint = NSLayoutConstraint.init(item: layoutView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 50.0)
//制約は定義した後、activateする必要あり
topConstraint.isActive = true
//制約を追加 left:50
let leftConstraint = NSLayoutConstraint.init(item: layoutView, attribute: .left, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1.0, constant: 50.0)
leftConstraint.isActive = true
//制約を追加 width:70
let widthConstraint = NSLayoutConstraint.init(item: layoutView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 70.0)
widthConstraint.isActive = true
//制約を追加 height:70
let heightConstraint = NSLayoutConstraint.init(item: layoutView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 70.0)
heightConstraint.isActive = true
2、NSLayoutAnchor
iOS9からは
NSLayoutAnchor
を使いNSLayoutConstraint
オブジェクトを作ることができました。ただ直接にNSLayoutConstraint
オブジェクトを作ることではなく、UIView
あるいはその子クラスやUILayoutGuide
のあるanchor
プロパティー値を利用します。これらのプロパティーはautolayout中の主要NSLayoutAttribute
に相当します。
そのため、NSLayoutAnchor
の子クラスでNSLayoutAttribute
を設定してもいいです。
制約メソッド:xxxView.xxxAnchor.constraint( )
//紫色のUIImageViewを作成し、viewに追加する
let purLayoutIcon = "layoutIcon"
let purLayoutImg = UIImage(named: purLayoutIcon)
let purLayoutView = UIImageView(image: purLayoutImg)
purLayoutView.backgroundColor = .purple
purLayoutView.layer.cornerRadius = 10
//AutoresizingMaskをAutoLayoutの制約に置き換えるかどうか指定する値(必ずfalse)
purLayoutView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(purLayoutView)
//制約を追加 top:150 left:50 width:70 height:70
purLayoutView.topAnchor.constraint(equalTo: view.topAnchor, constant: 150.0).isActive = true
purLayoutView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 50.0).isActive = true
purLayoutView.widthAnchor.constraint(equalToConstant: 70.0).isActive = true
purLayoutView.heightAnchor.constraint(equalToConstant: 70.0).isActive = true
NSLayoutConstraint
よりNSLayoutAnchor
のほうは簡潔です。
3、ScrollView中のautolayout
ScrollView
はセルフのcontentSize
があるため、autolayout使用時に留意したほうがいいです。
ScrollView
の子viewのサイズは即ちcontentSize
です。
制約はScrollView
の完全なレイアウトを決める必要があり、上下左右ともに必要です。また、ScrollView
の子viewの最大サイズも必ず制約条件を通じて確定値を得られます。
//scrollViewを作成し、viewに追加する
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.backgroundColor = .cyan
//ページ単位のスクロールを可能する
scrollView.isPagingEnabled = true
view.addSubview(scrollView)
scrollView.topAnchor.constraint(equalTo: purLayoutView.bottomAnchor, constant: 30.0).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
//safeAreaのボトム距離を使う
scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
let bgColor = [UIColor.red,UIColor.green,UIColor.blue]
var leftView:UIView? = nil
for i in 0..<3{
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = bgColor[i]
scrollView.addSubview(view)
if let left = leftView{
view.leftAnchor.constraint(equalTo: left.rightAnchor, constant: 0).isActive = true
}else{
view.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 0).isActive = true
}
view.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0).isActive = true
view.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0).isActive = true
view.widthAnchor.constraint(equalTo: scrollView.widthAnchor, multiplier: 1.0).isActive = true
view.heightAnchor.constraint(equalTo: scrollView.heightAnchor, multiplier: 1.0).isActive = true
leftView = view
}
//全ての子viewは上、下、左がscrollViewを相手として制約していたが、右のほうがまだ
//右方向の制約を追加
if let left = leftView{
left.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: 0).isActive = true
}