Help us understand the problem. What is going on with this article?

Visual Format Languageを使う【Swift3.0】

More than 3 years have passed since last update.

パッと見、訳の分からない文字列のせいで取っ付きにくい印象がありますが慣れてしまえば実はそうでもないんです。制約付けのコツは、ストーリーボードでの制約にも言えることですが自分が部品になったつもりになって「自分の居場所は何と何が決まれば確定するのだろうか...」と考えることかなと思います。

お約束

  • translatesAutoresizingMaskIntoConstraints = falseを忘れずに!
  • 制約を付けるのはaddSubviewの後です!

実践/サンプル

1.縦横サイズの指定

横サイズの指定
H:[{object}(=={value})]

縦サイズの指定
V:[{object}(=={value})]

※{object}はオブジェクトのディクショナリキーを指定します。以下同様

試してみましょう。次のコードはボタンをひとつ作成し、縦横サイズの指定をしています。横を示す「H:」は省略可能です。制約はaddConstraintsで設定します。メソッド名は複数形です。viewsパラメタにはオブジェクトのディクショナリを渡します。

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // ボタン
        let button = UIButton(type: UIButtonType.system)
        button.backgroundColor = UIColor.yellow
        button.setTitle("button", for: .normal)
        view.addSubview(button)

        // オブジェクトをディクショナリに格納
        let objects = ["button":button]

        objects.forEach { $1.translatesAutoresizingMaskIntoConstraints = false }

        button.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[button(==140)]", options: .alignAllTop, metrics: nil, views: objects))
        button.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[button(==50)]", options: .alignAllTop, metrics: nil, views: objects))


    }
}

実行結果
0.png

2.オフセットで位置を指定

左マージンを指定
H:|-{value}-[{object}]

上マージンを指定
V:|-{value}-[{object}]

右マージンを指定
H:[{object}]-{value}-|

下マージンを指定
V:[{object}]-{value}-|

左右マージンを指定
H:|-{value}-[{object}]-{value}-|

上下マージンを指定
V:|-{value}-[{object}]-{value}-|

「|」が隣接するオブジェクトの境界線、「-」が距離を示しているわけですね。

コードに以下を加えてみます。ボタンが外側(=View)から横50px、上から100pxの位置に表示されます。addConstraintsメソッドをコールしているのが相対関係にある外側のオブジェクト(view)であることに注意してください。

        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-50-[button]", options: .alignAllTop, metrics: nil, views: objects))
        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-100-[button]", options: .alignAllTop, metrics: nil, views: objects))

1.png

ちなみに左右のマージンを50pxにするには H:|-50-[button]H:|-50-[button]-50-| にします。

        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-50-[button]-50-|", options: .alignAllTop, metrics: nil, views: objects))

2.png

3.オブジェクト間での制約

オブジェクト2の横サイズをオブジェクト1の横サイズと同じにする
H:[{object1}(=={object2})]

オブジェクト2の縦サイズをオブジェクト1の縦サイズと同じにする
V:[{object1}(=={object2})]

オブジェクト2をオブジェクト1の右横に配置する
H:[{object1}]-{value}-[{object2}]

オブジェクト2をオブジェクト1の下に配置する
V:[{object1}]-{value}-[{object2}]

テキストフィールドをひとつ追加して、ボタンと同じサイズ、ボタンの横にマージン10pxを指定して表示させてみます。コードにテキストフィールドの生成処理を追加し、オブジェクト配列に追加します。

        // テキストフィールド
        let textField = UITextField()
        textField.borderStyle = .roundedRect
        textField.text = "textfield"
        view.addSubview(textField)


        // オブジェクトをディクショナリに格納
        let objects = ["button":button,"textField":textField]

        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[button(==textField)]", options: .alignAllBottom, metrics: nil, views: objects))
        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[button(==textField)]", options: .alignAllBottom, metrics: nil, views: objects))
        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[button]-10-[textField]", options: .alignAllBottom, metrics: nil, views: objects))

3.png

現在のコードから H:[button]-10-[textField]V:[button]-10-[textField] に変更するだけでテキストフィールドがボタンの下に表示されそうですが実はそれだけではダメです。上下の制約に変更した場合、optionsのパラメタ(揃えの基準)も適したものを設定しないといけません。「.alignAllLeft」か「.alignAllRight」にする必要があります。

4.各要素を一直線に並べたレイアウト

オブジェクト1とのマージンを確保しながらオブジェクト2の横サイズを可変にする
|-[{object1}]-[{object2}(>={value})]-|

ボタンの横マージンとテキストフィールドの横幅と位置の制約をコメントアウトして、横並びにする制約を加えます。数値指定のない「-」のみの場合は8ピクセルのマージンが付きます。この例ではテキストフィールドが20px以下にならない範囲で可変となります。

    //view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-50-[button]", options: .alignAllTop, metrics: nil, views: objects))
    //view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[button(==textField)]", options: .alignAllBottom, metrics: nil, views: objects))
    //view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[button]-10-[textField]", options: .alignAllBottom, metrics: nil, views: objects))
    view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "|-[button]-[textField(>=20)]-|", options: .alignAllBottom, metrics: nil, views: objects))

4.png

次のコードを加えてボタンの横幅を変えてみます。

    button.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[button(==50)]", options: .alignAllTop, metrics: nil, views: objects))

5.png

テキストフィールドの横幅も追従します。

テストコード全体

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.


        // ボタン
        let button = UIButton(type: UIButtonType.system)
        button.backgroundColor = UIColor.yellow
        button.setTitle("button", for: .normal)
        view.addSubview(button)

        // テキストフィールド
        let textField = UITextField()
        textField.borderStyle = .roundedRect
        textField.text = "textfield"
        view.addSubview(textField)


        // オブジェクトをディクショナリに格納
        let objects = ["button":button,"textField":textField]

        objects.forEach { $1.translatesAutoresizingMaskIntoConstraints = false }

        // オブジェクト配列
        let objects = ["button":button,"textField":textField]

        objects.forEach { $1.translatesAutoresizingMaskIntoConstraints = false }

        button.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[button(==140)]", options: .alignAllTop, metrics: nil, views: objects))
        button.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[button(==50)]", options: .alignAllTop, metrics: nil, views: objects))
        //view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-50-[button]", options: .alignAllTop, metrics: nil, views: objects))
        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-100-[button]", options: .alignAllTop, metrics: nil, views: objects))
        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[button(==textField)]", options: .alignAllBottom, metrics: nil, views: objects))
        //view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[button(==textField)]", options: .alignAllBottom, metrics: nil, views: objects))
        //view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[button]-10-[textField]", options: .alignAllBottom, metrics: nil, views: objects))
        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "|-[button]-10-[textField(>=20)]-|", options: .alignAllBottom, metrics: nil, views: objects))
        button.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[button(==50)]", options: .alignAllTop, metrics: nil, views: objects))


    }
}
fromage-blanc
楽曲制作(ベリバービリバー/石川ひとみ等)と開発(Webシステム、iPhoneアプリ)を生業にしてます。
https://fromage-blanc-recette.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした