SwiftUIの登場により、autolayoutの関連性は急速に低下していますが、このメカニズムは依然として積極的に使用されており、ライブラリはコードで直接UIを作成(または変更)するユーザーに役立ちます。
インターフェイスを構築するこの方法には、その使用を制限するいくつかの欠点があります。
- NSLayoutConstraint要素の作成は非常に不便です。
- 視認性が悪い-コードを見ると、UIがどのように見えるかを理解するのは困難です。
- 多数のルーチンコード。各ビューを配置するには、平均約3つの制約が必要です。同じタイプのコードの3行。
- 動的に変化するインターフェイスの作成の複雑さ:制約を個別の変数に保存して、それらを変更できるようにする必要があります。また、冗長な制約を作成して不要な制約をオフにすることもよくあります。
最初の問題は、より人道的なもので制約を作成するための標準的な方法をラップすることで簡単に解決できます。そして、これは例えばSnapKit、TinyConstraintsや他の同様のライブラリですでに十分に実装されています。
しかし、それでも、同じタイプのコードをたくさん書く必要があり、レイアウトの可視性と動的な変更に問題があります。 UIStackViewはこれらの問題を巧妙に解決しますが、残念ながら、UIStackViewは個々の要素の位置のカスタマイズを非常に制限しています。そのため、サブビューのスタックレイアウトを制御するが、各サブビューの場所を個別にカスタマイズする機能を備えたコンテナーUIViewのアイデアが生まれました。
これはBoxViewの背後にあるアプローチであり、非常に効果的であることが証明されています。 BoxViewを使用すると、手動での制約の作成をほぼ完全になくすことができます。ユーザーインターフェイスのほぼ全体が、ネストされたBoxViewのシステムとして形成されます。その結果、コードが大幅に短くなり、見やすくなりました。動的UIの場合、その効果は特に顕著です。
BoxViewは多くの点で標準のUIStackViewに似ていますが、サブビューを配置するためのさまざまなルールを使用します。サブビューごとにインデントとサイズを個別に設定できます。レイアウトを作成するために、BoxViewはBoxItemsの配列を使用します。これには、表示する必要があるすべてのビューと、それらの配置方法に関する情報が含まれています。そして、これは多くのコードをまったく必要としません-ほとんどのレイアウトパラメータはデフォルトで取得され、必要な値のみが明示的に指定されます。
BoxViewの本質的なプロパティは、追加されたサブビューに対して指定された制約のみを作成し、それ以外は何も作成しないことです。したがって、他のライブラリやレイアウト方法と組み合わせて、制限なく使用できます。
例として、BoxViewを使用して単純なログインフォームを作成することを検討してください(段階的な説明を含む完全なサンプルコードは、githubのBoxViewExampleプロジェクトで入手できます)。
BoxViewでこのようなレイアウトを作成するには、数行のコードで十分です。
nameBoxView.items = [nameImageView.boxed.centerY(), nameField.boxed]
passwordBoxView.items = [passwordImageView.boxed.centerY(), passwordField.boxed]
boxView.insets = .all(16.0)
boxView.spacing = 20.0
boxView.items = [
titleLabel.boxed.centerX(padding: 30.0).bottom(20.0),
nameBoxView.boxed,
passwordBoxView.boxed,
forgotButton.boxed.left(>=0.0),
loginButton.boxed.top(30.0).left(50.0).right(50.0),
]
BoxItem要素は、ボックス化変数を使用して任意のUIViewから作成されます。その後、4辺のパディング、配置、絶対サイズまたは相対サイズに設定できます。
レイアウトの要素は、残りの配置に影響を与えることなく、自由に追加および削除できます(アニメーションを含む)。例として、空の入力フィールドのチェックを追加してみましょう。エラーの場合は、空のフィールドのすぐ下にメッセージを表示します。
メッセージは既存のレイアウトに「統合」されるはずですが、既存のコードを変更する必要さえありません!
func showErrorForField(_ field: UITextField) {
errorLabel.frame = field.convert(field.bounds, to: boxView)
let item = errorLabel.boxed.top(-boxView.spacing).left(errorLabel.frame.minX - boxView.insets.left)
boxView.insertItem(item, after: field.superview, z: .back)
boxView.animateChangesWithDurations(0.3)
}
@objc func onClickButton(sender: UIButton) {
for field in [nameField, passwordField] {
if field.text?.isEmpty ?? true {
showErrorForField(field)
return
}
}
// ok, can proceed with login
}
@objc func onChangeTextField(sender: UITextField) {
errorLabel.removeFromSuperview()
boxView.animateChangesWithDurations(0.3)
}
BoxViewはすべての自動レイアウトツールをサポートします。要素間の距離、絶対サイズと相対サイズ、優先度、RTL言語のサポート。 UIViewに加えて、非表示オブジェクト-UILayoutGuidesもレイアウト要素として使用できます。フレックスレイアウトも使用できます。もちろん、レイアウトスキーム自体は、UIViewのネストされたスタックのシステムの形で、要素の相対的な配置について考えられるすべてのオプションを100%カバーするわけではありませんが、これは必須ではありません。通常のユーザーインターフェイスの大部分は問題ありませんが、より特殊なケースでは、対応する追加の制約を他の方法でいつでも追加できます。たとえば、アスペクト比の制約を作成するためのいくつかのユーティリティメソッドもライブラリに含まれています。
別の小さな例github(〜100行のコード!)で利用可能です。BoxViewネストシステムを他の制約設定メソッドと組み合わせて使用する方法と、BoxView設定のアニメーションによる変更を示します。
github上のBoxViewプロジェクト