【Swift】NSLayoutConstraint をコードで自在に

iOS, Swift

この記事は最終更新日から1年以上が経過しています。

はじめに

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のイニシャライザを見てみます。少し見易くしたものを以下に記載します。

項目がてんこ盛りすぎて、私はスッとページを閉じました。

ここで、Appleの公式ドキュメントに載っている画像を見てみます。

view_formula_2x_from_appleDeveloper.png
(引用:Apple Programmatically Creating Constraints

この画像を見ても、初めて見たときは、なんのことやらでした。

では、イニシャライザとStoryboard上の設定を見比べてみましょう。

以下の画像は、制約をクリックした際に右端に表示される画面です。

スクリーンショット 2020-12-23 11.51.07.png

似たような項目があります。1つずつ見ていきましょう。

First Item

イニシャライザでは、 item view1: Anyattribute attr1: NSLayoutConstraint.Attribute にあたります。

  • item view1: Any : SuperView
  • attribute 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? : BlueView
  • attribute attr2: NSLayoutConstraint.Attribute : Trailing

Constant

イニシャライザでは、 constant c: CGFloat にあたります。

Multiplier

イニシャライザでは、 multiplier: CGFloat にあたります。

こう見比べていくと、イニシャライザで何を設定しているのか読み取りやすくなると思います。
Apple公式ドキュメントの画像と比較すると、さらに理解が深まるのではないかと思います。
では、実際に制約をつけてみましょう。

実際に、制約をつけてみた

パターン1 NSLayoutConstraint のイニシャライザを使用した場合

パターン2 NSLayoutAnchor を使用した場合

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]) で一気に設定するようにしましょう。

スクリーンショット

Simulator Screen Shot - iPhone 12 Pro Max - 2020-12-24 at 13.07.00.png

※ translatesAutoresizingMaskIntoConstraints について

AutoresizingMaskの設定値をAuto Layoutの制約に変換するかどうかを決めるBool値です。
Auto Layout登場以前はAutoresizingMaskという仕組みで、ビューの動的なサイズ変更を実現していたそうです。
このBool値を予め false にしておかないと、自分で設定した制約とコンフリクトを起こしてしまいます。
(参考サイト: Qiita – Auto Layoutをコードで書いて学ぶ(Swift 2.2))

AspectRatio の制約の付け方

本記事では、AspectRatio (縦 : 横)9 : 16 で設定しようと思います。

パターン1 NSLayoutConstraint のイニシャライザを使用した場合

  • itemtoItem には、AspectRatio を設定したいViewを記述します。

  • attribute は、縦横比なので、 widthheight を設定します。

    • 縦:横(縦 / 横)で計算したい場合
      itemattributeheight
      toItemattributewidth
  • multiplier には、比率の計算結果を入力します。

コード

上記コードでは、大きさをSuperViewより小さいサイズに設定する為、BottomAnchor を設定していません。設定してしまうと、警告文が表示されます。
以下がBottomAnchorを設定した上で、AspectRatioを設定した時に出る警告文です。

パターン2 NSLayoutAnchor を使用した場合

縦 : 横にしたいので、メソッドを呼び出す側を heightAnchor 、メソッドの引数の equalTowidthAnchor を指定します。

コード

どちらのパターンでも結果は同じになります。
AspectRationの設定の場合でも、パターン2の方がコードが簡潔で見やすいです。
以下が実行結果のスクリーンショットになります。

スクリーンショット

Simulator Screen Shot - iPhone 12 Pro Max - 2020-12-24 at 12.15.54.png

NSLayoutAnchorを使う利点・注意点

NSLayoutAnchorの公式ドキュメントを見てみると。利点と注意点についての記載があります。

利点

以下、公式ドキュメント記載のコードと、説明文です。

コード

説明文

As you can see from these examples, the NSLayoutAnchor class provides several advantages over using the NSLayoutConstraint API directly.

  • The code is cleaner, more concise, and easier to read.
  • The NSLayoutConstraint.Attribute subclasses provide additional type checking, preventing you from creating invalid constraints.

上記説明文には、以下のように書かれています。

例を見てわかるとおり、 NSLayoutConstraintAPIを使うよりも、 NSLayoutAnchorを使うといくつかの利点があります。

  • コードが綺麗になって、とても見やすくなります。
  • 追加の型チェックを行います。無効な制約の作成を防いでくれます。

注意点

While the NSLayoutAnchor class provides additional type checking, it is still possible to create invalid constraints.

For example, the compiler allows you to constrain one view’s leadingAnchor with another view’s leftAnchor since they are both NSLayoutXAxisAnchor instances.

However, Auto Layout does not allow constraints that mix leading and trailing attributes with left or right attributes.

As a result, this constraint crashes at runtime.

NSLayoutAnchorクラスは追加の型チェックを行いますが、無効な制約を作成できてしまいます。

その例が、あるViewの leadingAnchorともう一つのViewの leftAnchorで制約を作成した時です。どちらも NSLayoutXAxisAnchorのインスタンスなので、制約を作成することができてしまいます。

しかしながら、Auto Layoutでは、 NSLayoutConstraint.Attributeが enum の case で管理されているので、あるViewの .leading と、あるViewの .left を指定することはできません。

case .left = 1case .leading = 5なのでエラーが出てクラッシュします。

最後に

最初は、制約をコード上で設定することに抵抗がありました。

しかしながら、1つずつ丁寧に、設定しなければいけない項目を確認すれば、何ら難しいことではありませんでした。

本記事でご紹介した、パターン1(イニシャライザ)は理解を深めるためにご紹介したものです。NSLayoutAnchorを使用すればコード量も減るので、是非パターン2をご活用ください。

少しでも理解の助けになればと思います。

最後まで記事を読んで頂き、ありがとうございました。

参考サイト一覧

iOS, Swift