はじめに
2020年度入社のiOSエンジニア、澁谷太智です。 今回は、制約をコード上で設定する方法についてご紹介しようと思います。
コード上で制約をつけられることは知っていましたが、記述量が多くて難しそうという印象が強く、なかなか手をつけられずにいました。研修中の課題で、コード上で制約をつける機会があったので、その時調べたことを記事にしようと思いました。
では、制約の付け方について、1つずつ丁寧に見ていきたいと思います。
TOC(Table Of Contents)
- NSLayoutConstraintのイニシャライザについて
- 実際に、制約をつけてみた
- AspectRatio の制約の付け方
- NSLayoutAnchorを使う利点・注意点
- 最後に
- 参考サイト一覧
作業環境
- macOS Catalina
- version 10.15.5
- Xcode
- version 12.3
- iPhone12 Pro Max (シミュレーター)
- iOS 14.3
- Swift
- version 5.3.2
NSLayoutConstraintのイニシャライザについて
Appleの公式ドキュメントが参考になるので、ご紹介します。 Apple Developer – Programmatically Creating Constraints
さらに、その他のConstraint (LabelのHaggingなど) に関する情報が載っている公式ドキュメントもご紹介します。 Apple Developer – Anatomy of a Constraint
まず、NSLayoutConstraintのイニシャライザを見てみます。少し見易くしたものを以下に記載します。
convenience init(
item view1: Any,
attribute attr1: NSLayoutConstraint.Attribute,
relatedBy relation: NSLayoutConstraint.Relation,
toItem view2: Any?,
attribute attr2: NSLayoutConstraint.Attribute,
multiplier: CGFloat,
constant c: CGFloat
)
項目がてんこ盛りすぎて、私はスッとページを閉じました。
ここで、Appleの公式ドキュメントに載っている画像を見てみます。

(引用:Apple Programmatically Creating Constraints)
この画像を見ても、初めて見たときは、なんのことやらでした。
では、イニシャライザとStoryboard上の設定を見比べてみましょう。
以下の画像は、制約をクリックした際に右端に表示される画面です。

似たような項目があります。1つずつ見ていきましょう。
First Item
イニシャライザでは、
item view1: Any と attribute attr1: NSLayoutConstraint.Attribute にあたります。
item view1: Any: SuperViewattribute attr1: NSLayoutConstraint.Attribute: Trailing
Relation
イニシャライザでは、
relatedBy relation: NSLayoutConstraint.Relation にあたります。
relatedBy relation: NSLayoutConstraint.Relation: Equal
Second Item
イニシャライザでは、
toItem view2: Any? と attribute attr2: NSLayoutConstraint.Attribute にあたります。
toItem view2: Any?: BlueViewattribute attr2: NSLayoutConstraint.Attribute: Trailing
Constant
イニシャライザでは、
constant c: CGFloat にあたります。
Multiplier
イニシャライザでは、
multiplier: CGFloat にあたります。
こう見比べていくと、イニシャライザで何を設定しているのか読み取りやすくなると思います。 Apple公式ドキュメントの画像と比較すると、さらに理解が深まるのではないかと思います。 では、実際に制約をつけてみましょう。
実際に、制約をつけてみた
パターン1 NSLayoutConstraint のイニシャライザを使用した場合
import UIKit
final class ViewController: UIViewController {
private let newView: UIView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
//AutoresizingMask を Auto Layout変換 無効化
newView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(newView)
let leading = NSLayoutConstraint(
item: newView,
attribute: .leading,
relatedBy: .equal,
toItem: view,
attribute: .leading,
multiplier: 1,
constant: 0
)
let trailing = NSLayoutConstraint(
item: newView,
attribute: .trailing,
relatedBy: .equal,
toItem: view,
attribute: .trailing,
multiplier: 1,
constant: 0
)
let top = NSLayoutConstraint(
item: newView,
attribute: .top,
relatedBy: .equal,
toItem: view,
attribute: .top,
multiplier: 1,
constant: 0
)
let bottom = NSLayoutConstraint(
item: newView,
attribute: .bottom,
relatedBy: .equal,
toItem: view,
attribute: .bottom,
multiplier: 1,
constant: 0
)
// 制約を有効化します
NSLayoutConstraint.activate([leading, trailing, top, bottom])
view.backgroundColor = .black
newView.backgroundColor = .blue
}
}
パターン2 NSLayoutAnchor を使用した場合
import UIKit
final class ViewController: UIViewController {
private let newView: UIView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
//AutoresizingMask を Auto Layout変換 無効化
newView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(newView)
let leading = newView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0)
let trailing = newView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0)
let top = newView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0)
let bottom = newView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0)
//制約を有効化します
NSLayoutConstraint.activate([leading, trailing, top, bottom])
view.backgroundColor = .black
newView.backgroundColor = .blue
}
}
2つの方法、どちらで制約を付けても結果は同じです。パターン2に関してはiOS9から使えるようになりました。 コード量が減って、パターン2の方が見やすいですね。
上記2つのコードで、制約の有効化に、
NSLayoutConstraint.activate([NSLayoutConstraint]) を使用しています。
公式ドキュメントを見てみると、
Typically, using this method is more efficient than activating each constraint individually.
個別に制約を有効化するより、
NSLayoutConstraint.activate([NSLayoutConstraint]) を使用した方が効率が良いと書いてあります。
複数の制約を設定するときは、
isActive で個々に設定するのではなく、NSLayoutConstraint.activate([NSLayoutConstraint]) で一気に設定するようにしましょう。
スクリーンショット

※ translatesAutoresizingMaskIntoConstraints について
AutoresizingMaskの設定値をAuto Layoutの制約に変換するかどうかを決めるBool値です。
Auto Layout登場以前はAutoresizingMaskという仕組みで、ビューの動的なサイズ変更を実現していたそうです。
このBool値を予め
false にしておかないと、自分で設定した制約とコンフリクトを起こしてしまいます。
(参考サイト: Qiita – Auto Layoutをコードで書いて学ぶ(Swift 2.2))
AspectRatio の制約の付け方
本記事では、 AspectRatio (縦 : 横)9 : 16 で設定しようと思います。
パターン1 NSLayoutConstraint のイニシャライザを使用した場合
let ratio: CGFloat = 9 / 16
let aspectRatio: NSLayoutConstraint = NSLayoutConstraint(
item: newView,
attribute: .height,
relatedBy: .equal,
toItem: newView,
attribute: .width,
multiplier: ratio,
constant: 0
)
aspectRatio?.isActive = true
item、toItemには、AspectRatio を設定したいViewを記述します。attributeは、縦横比なので、widthとheightを設定します。- 縦:横(縦 / 横)で計算したい場合
itemのattributeはheighttoItemのattributeはwidth
- 縦:横(縦 / 横)で計算したい場合
multiplierには、比率の計算結果を入力します。
コード
import UIKit
final class ViewController: UIViewController {
private let newView: UIView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
newView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(newView)
let leading = newView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0)
let trailing = newView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0)
let top = newView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0)
let ratio: CGFloat = 9 / 16
let aspectRatio = NSLayoutConstraint(
item: newView,
attribute: .height,
relatedBy: .equal,
toItem: newView,
attribute: .width,
multiplier: ratio,
constant: 0
)
// 制約を有効化
NSLayoutConstraint.activate([leading, trailing, top, aspectRatio])
view.backgroundColor = .black
newView.backgroundColor = .blue
}
}
上記コードでは、大きさをSuperViewより小さいサイズに設定する為、BottomAnchor を設定していません。設定してしまうと、警告文が表示されます。 以下がBottomAnchorを設定した上で、AspectRatioを設定した時に出る警告文です。
澁谷太智