はじめに
最近 NSLayoutAnchor を利用していますが、isActive = true
や translatesAutoresizingMaskIntoConstraints = false
をよく忘れます
また以前は SnapKit を利用していて、制約が壊れた際のログが以下のように出力されるので、どのファイルのどの行当たりを見ればいいかが一目瞭然でとても助かっていました。
(
"<SnapKit.LayoutConstraint:0x6100000a32a0@ViewController.swift#90 UIView:0x7fee58409540.left == UIView:0x7fee58400c60.left + 22.0>",
"<SnapKit.LayoutConstraint:0x6100000a3540@ViewController.swift#92 UIView:0x7fee58409540.right == UIView:0x7fee58400c60.right - 22.0>",
"<SnapKit.LayoutConstraint:0x6100000a35a0@ViewController.swift#93 UIView:0x7fee58409540.width == 300.0>",
"<NSLayoutConstraint:0x618000097bb0 'UIView-Encapsulated-Layout-Width' UIView:0x7fee58400c60.width == 768 (active)>"
)
AutolayoutHelper
ということで NSLayoutAnchor
を薄くラップしつつ、デバッグしやすいライブラリを作ってみました。
インストール
github "sora0077/AutolayoutHelper" "master"
※ 今のところ Carthage のみの対応です。
使い方
- import すると
UIView
,UILayoutGuide
などNSLayoutAnchor
を持っている型にautolayout
, (autoresizing
) プロパティが生えます。 - 基本的には
NSLayoutAnchor
とほぼ同じインターフェースです。- 追加として、よく使うサイズ指定や親ビューにフィットする指定のために
size
,edges
などが追加されています。
- 追加として、よく使うサイズ指定や親ビューにフィットする指定のために
例)
import UIKit
import AutolayoutHelper
class ViewController: UIViewController {
class View: UIView {}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let orangeBox = UIView()
orangeBox.backgroundColor = .orange
view.addSubview(orangeBox)
orangeBox.autolayout.edges.equal(to: view.autoresizing.edges, insets: (x: 50, y: 100))
let redBox = UIView()
redBox.backgroundColor = .red
view.addSubview(redBox)
redBox.autolayout.top.equal(to: orangeBox.autolayout.top, constant: 50)
redBox.autolayout.left.equal(to: orangeBox.autolayout.left)
let sizeConstraint =
redBox.autolayout.size.equal(to: orangeBox.autolayout.size, insets: (x: 50, y: 0), priority: .defaultHigh)
redBox.autolayout.bottom.equal(to: orangeBox.autolayout.bottom)
print(sizeConstraint.width, sizeConstraint.height) // NSLayoutConstraint, NSLayoutConstraint
}
}

デバッグ
もし制約が満たせていない場合、以下のようなログ表示になります。
(
"<NSLayoutConstraint:0x600000098e70 '@ViewController.swift#22' H:|-(50)-[UIView:0x7f981ec05270](LTR) (active, names: '|':UIView:0x7f981ec05670 )>",
"<NSLayoutConstraint:0x600000098f10 '@ViewController.swift#22' UIView:0x7f981ec05270.trailing == UIView:0x7f981ec05670.trailing - 50 (active)>",
"<NSLayoutConstraint:0x6000000990a0 '@ViewController.swift#23' UIView:0x7f981ec05270.width == 30 (active)>",
"<NSLayoutConstraint:0x608000095db0 'UIView-Encapsulated-Layout-Width' UIView:0x7f981ec05670.width == 414 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x6000000990a0 '@ViewController.swift#23' UIView:0x7f981ec05270.width == 30 (active)>
実装
登場するのは基本的に Layout<Anchors>
だけです。
public struct Layout<Anchors> {
let anchors: Anchors
}
この型に対して制約付きの extension
を必要な分だけ定義しています。
例えば size
に対する実装は以下になります。
// UIView.autolayout
var size: Layout<(width: NSLayoutDimension, height: NSLayoutDimension)> { get }
public extension Layout where Anchors == (width: NSLayoutDimension, height: NSLayoutDimension) {
@discardableResult
func equal(to other: Layout,
multiplier: CGFloat = 1,
constant: CGFloat = 0,
priority: LayoutPriority = .required,
file: StaticString = #file,
line: UInt = #line
) -> (width: NSLayoutConstraint, height: NSLayoutConstraint) {
return (
width: anchors.width.constraint(equalTo: other.width, ...),
height: anchors.height.constraint(equalTo: other.height, ...),
)
}
}
Anchors
が2つの NSLayoutDimension
であるときに同じ型を引数に持つ equal
メソッドを定義しています。
全体は こちら です。
補足
使い方で出てきた autoresizing
プロパティですが、実態は以下のように translatesAutoresizingMaskIntoConstraints
を変更しているかどうかの違いです。
これについては、あまり良いインターフェースではない気がするので今後無くすかもしれません。
public extension AutolayoutExtension where Self: UIView {
var autolayout: Extension<Self> {
if translatesAutoresizingMaskIntoConstraints {
translatesAutoresizingMaskIntoConstraints = false
}
return Extension(base: self)
}
var autoresizing: Extension<Self> {
return Extension(base: self)
}
}