0. はじめに
今回はiOSのログインのViewをAuto Layoutをコードベースで定義して、使って行こうと思います。
アイコンや背景色などコンテンツが必要な箇所は実装できないので、省略しています。
1. Auto Layoutとは
Auto Layoutとは
Viewの相関関係に基づいて制約をすることによってレイアウトする手法です。
1.0 なんで大事なの
一言でまとめるとレスポンシブのためです。
iOSデバイスは昔と違って、いろんな大きなの種類があります。
それら一つ一つにレイアウトを決めても効率が悪いので、まとめてレイアウトしようというわけです。
1.1 制約とは
制約とは、Auto Layoutに用いるインターフェースオブジェクトの位置や大きさの関係性のことです。
自然言語でいうと「この長方形のオブジェクトを縦10px, 横12pxで中央揃えして」といったようなことが制約に当たります。
1.2 制約式とは
制約式とは制約を実際のコードで定義したものです。
特にAuto Layoutでは、このような制約式の連立方程式を解いて、レイアウトを定めています。
具体的には
Botton1.top = Botton2.top + 8
のようなものです。他のインターフェースオブジェクト以外にも、親クラスsuperview
なども参照することができます。
1.3 制約の定義方法
制約を定義するには以下の二つの方法があります。
NSLayoutAnchor
を使う方法Intrinsic Content Size
を使う方法
1.3.1 NSLayoutAnchor
NSLayoutLayoutとは、iOS9から追加された制約を制定するためのクラスで、開発者が明示的に制約オブジェクトを生成します。
詳しくは知りませんが、昔はNSLayoutConstrain
だったみたいですが、利便性と安全性を備えたものに進化したみたいです。
公式ページは以下のようにのなっています。
良記事は以下のものです。
1.3.2 Intrinsic Content Size
Intrinsic Content Sizeとは、あらかじめオブジェクトの持つコンテンツによって決定される制約の定義方法です。
たとえば二つのUITextField
のテキストフィールドを作ったとして
ひとつは
a
でもう一つは
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
とすると、二つ目の方が小さくなります。
1.4 xibファイル
xib
ファイルとはApple独自のxmlフォーマットでStoryBoardと同等の役割をします。
2. 実際に作る
2.1 配置は考えずにフォームを作成
まず、フォームを作成します。
せっかくAuto Layoutを使うので、コードベースで生成したいと思います。
まず、インターフェースオブジェクトを生成します。
let button = UIButton()
仮によくわからないオブジェクトはShow Quick help
でヘルプを見ることができます。
また、もっと詳細は一番下のClass Reference
で見ることができます。
特にUIButton
には以下のようなプロパティを持つ。
- 外接矩形: インターフェースオブジェクトから装飾を除いたもの
- フレーム: 装飾までを含めた大きさ
ここでCGReck
型で指定することになります。
ここで自分のviewの中央に配置したいので、self.view.bounds.width/2
などと定義します。
ここでbounds
とframe
の違いが大事なので以下の記事を参考にしてください。
簡単にまとめると
term | 説明 |
---|---|
frame | 親ビューの座標系からみての座標 |
bounds | 自身の座標からみての自分 |
つまり必ずbounds.origin.x
は0であり、y座標も然りです。
また、デフォルトでは、背景もUIColor.white
、文字色もUIColor.white
なので、初期画面では何も見えません。
それはCGRect
の仕様で、以下のように座標をとるからです。
ボタンを配置まで、できあがったコードが以下の通りです。
class LoginViewController: UIViewController let formWidth:CGFloat = 300 let formHeigth:CGFloat = 40 let button = UIButton() override func viewDidLoad() { super.viewDidLoad() button.setTitle("SIGN IN", for: UIControlState.normal) button.backgroundColor = UIColor.black button.frame = CGRect(x: (self.view.bounds.width - formWidth) / 2, y: self.view.bounds.height / 2, width: formWidth, height: formHeigth) self.view.addSubview(button) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } }
ビルドすると以下のようになってます。ここまでは目的通りです。
ここでUITextField
を作っていきます。
UIButton
と全く一緒です。それぞれにプレースホズダーを設定しました。
次にUIImage
をロゴ用に配置したいと思います。
無料でロゴを作れるサービス「hatchful」を使いました。
他のものと同様に制約を課してみます。
また、Assets Catalog
に追加します。
UIImage
は「画像がない」のケースがあるので初期化するとオプショナル型が返ってきます。
一応、オプショナルの使い方は以下の通りです。
するとこのようになりました。
また、self.[オブジェクト].frame
とするとCGRect
を渡すことになるので、CGSize
で高さと横幅だけ指定しておきます。
以下の項目を
self.passwordTextField.frame = CGRect(x: (self.view.bounds.width - formWidth) / 2, y: self.view.bounds.height / 2 - 60, width: formWidth, height: 30) // を self.passwordTextField.frame.size = CGSize(width: formWidth, height: 30)
に変更します。
すべて原点にあるので、以下のようになります。
2.2 制約を課す
制約に関しての公式サイトは以下の通りです。
以下のような構成にしたいと思っています。 コンテンツはすべて中央揃えにしたいです。
以下のように制約をかけたいオブジェクトのプロパティとしてAnchor
を指定します。
また、すべてのUIView
を継承しているオブジェクトは使うことができます。
以下のようの種類を指定できます。
leadingAnchor: NSLayoutXAxisAnchor trailingAnchor: NSLayoutXAxisAnchor leftAnchor: NSLayoutXAxisAnchor rightAnchor: NSLayoutXAxisAnchor topAnchor: NSLayoutYAxisAnchor bottomAnchor: NSLayoutYAxisAnchor widthAnchor: NSLayoutDimension heightAnchor: NSLayoutDimension centerXAnchor: NSLayoutXAxisAnchor centerYAnchor: NSLayoutYAxisAnchor firstBaselineAnchor: NSLayoutYAxisAnchor //UILayoutGuideにはない lastBaselineAnchor: NSLayoutYAxisAnchor //UILayoutGuideにはない
以下の画像が参考になります。
topAnchor
: 上leadingAnchor
: 読む方向によって決まるはじまりtrailingAnchor
: 読む方向によって決まるはじまりbottomAnchor
: 下leftAnchor
: 左rightAnchor
: 右
centerXAnchor
: X方向の中心線centerYAnchor
: Y方向の中心線widthAnchor
: 幅heightAncher
: 縦
一応、補足のためにもう一つ、付け足すとすると以下の画像です。
そのほかにも
firstBaselineAnchor
: テキストの最初の行のbaseline
lastBaselineAnchor
: テキストの最後の行のbaseline
以下のような画像がわかりやすいです。
[https://cdn-images-1.medium.com/max/1600/1ky1FYn3hymW0ajz_tQ8Wow.png:image=https://cdn-images-1.medium.com/max/1600/1ky1FYn3hymW0ajz_tQ8Wow.png]
Autolayouts via Layout anchors – Hassan Ahmed Khan – Medium
また、Anchor
のメソッドは
// thisAnchor = otherAnchor func constraint(anchor: NSLayoutAnchor!) -> NSLayoutConstraint! // thisAnchor = otherAnchor + constant func constraint(anchor: NSLayoutAnchor!, constant c: CGFloat) -> NSLayoutConstraint!
となっています。
まず実装して見ます。
どうやらaddSubView
を追加してからじゃないと制約はかけることができないみたいです。
結果として
// MARK: - Constrains self.imageView.widthAnchor.constraint(equalToConstant: imageViewSide).isActive = true self.imageView.heightAnchor.constraint(equalToConstant: imageViewSide).isActive = true self.imageView.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor).isActive = true self.imageView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 120).isActive = true self.emailTextField.widthAnchor.constraint(equalToConstant: formWidth).isActive = true self.emailTextField.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor).isActive = true self.emailTextField.topAnchor.constraint(equalTo: self.imageView.bottomAnchor, constant: 10).isActive = true self.passwordTextField.widthAnchor.constraint(equalToConstant: formWidth).isActive = true self.passwordTextField.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor).isActive = true self.passwordTextField.topAnchor.constraint(equalTo: self.emailTextField.bottomAnchor, constant: 40).isActive = true self.button.widthAnchor.constraint(equalToConstant: formWidth).isActive = true self.button.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor).isActive = true self.button.topAnchor.constraint(equalTo: self.passwordTextField.bottomAnchor, constant: 50).isActive = true
のようにすると以下のようになります。
2.3 制約をつける
どの制約が勝つかを定義しなければ、おなじ優先度ならば競合してしまうでしょう。
3. AutoLayoutのデバッグ
以下のサイトでデバック方法がかかれてあります。
3.1 レイアウト崩れの例
レイアウトが崩れるのは以下の二つがあります。
- 曖昧な制約:レイアウトが決まりきらない制約
- 制約のコンフリクト:制約の条件が衝突しているもの
3.2 曖昧な制約
以下で曖昧なものがあるかを調べられます。
self.view.hasAmbiguosLayout()
3.3 コンフリクトを確認する
以下のようにエラーが出ればコンフリクトしています。
2018-08-31 15:03:44.451155+0900 Apacheek[17051:7788013] [LayoutConstraints] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) ( "<NSAutoresizingMaskLayoutConstraint:0x60800028b7c0 h=--& v=--& UIButton:0x7f9a899756e0'SIGN IN'.midX == 150 (active)>", "<NSLayoutConstraint:0x60000028d2f0 UIButton:0x7f9a899756e0'SIGN IN'.centerX == UILayoutGuide:0x6000001b0680'UIViewSafeAreaLayoutGuide'.centerX (active)>", "<NSAutoresizingMaskLayoutConstraint:0x60800028ce90 h=-&- v=-&- 'UIView-Encapsulated-Layout-Left' UIView:0x7f9a875172a0.minX == 0 (active, names: '|':UIWindow:0x7f9a89976620 )>", "<NSLayoutConstraint:0x60800028cdf0 'UIView-Encapsulated-Layout-Width' UIView:0x7f9a875172a0.width == 414 (active)>", "<NSLayoutConstraint:0x60000028be50 'UIViewSafeAreaLayoutGuide-left' H:|-(0)-[UILayoutGuide:0x6000001b0680'UIViewSafeAreaLayoutGuide'](LTR) (active, names: '|':UIView:0x7f9a875172a0 )>", "<NSLayoutConstraint:0x60000028d570 'UIViewSafeAreaLayoutGuide-right' H:[UILayoutGuide:0x6000001b0680'UIViewSafeAreaLayoutGuide']-(0)-|(LTR) (active, names: '|':UIView:0x7f9a875172a0 )>" ) Will attempt to recover by breaking constraint <NSLayoutConstraint:0x60000028d2f0 UIButton:0x7f9a899756e0'SIGN IN'.centerX == UILayoutGuide:0x6000001b0680'UIViewSafeAreaLayoutGuide'.centerX (active)> Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
4. まとめ
特に、StoryBoardを見ながら開発していると、macbookでは小さく感じてしまうことがありました。
エンジニアとしても宣言することは非常に価値があるので、これからもコードでしっかり書いていこうと思います。
5. 次回予告
translatesAutoresizingMaskIntoConstraints
やレイアウトの階層などをまとめたいと思います。
参考文献
**以下の文献はかなり古いので、注意が必要です。

よくわかるAuto Layout iOSレスポンシブデザインをマスター
- 作者: 川邉雄介,所友太
- 出版社/メーカー: リックテレコム
- 発売日: 2016/06/17
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る