通常の Stack を使うと、その中でしか配置を揃えることができませんから、非常に有効な機能です。ただ、alignmentGuide で揃えることができるケースは限定されています。
できることとできないことを説明していきます。
Sponsor Link
alignmentGuide で揃えることができる例
複数のビューが異なる Stack 内に存在し、そのビューの配置を揃えたいとき
以下の例では、テキストを水平方向に2つ配置し、それを垂直方向に2つ重ねています。
# わかりやすくするために、各ビューにボーダーを設定して表示しています。
struct ContentView: View {
@State private var name = ""
@State private var mail = ""
var body: some View {
VStack(alignment: .field) {
HStack() {
Text("Name")
.border(Color.blue)
TextField("name", text: $name)
.frame(maxWidth: 300)
.alignmentGuide(.field) { (d) -> CGFloat in return d[.leading]}
.border(Color.green)
}
HStack() {
Text("Mail")
.border(Color.blue)
TextField("mail", text: $mail)
.frame(maxWidth: 300)
.alignmentGuide(.field) { (d) -> CGFloat in return d[.leading]}
.border(Color.green)
}
}
.border(Color.red)
}
}
このケースでは、Text と TextField は、(共通の親ビューである) HStack の alignment を指定して、設定することができます。
しかし、TextField 同士の配置を揃えることは、HStack, VStack いずれの alignment を使っても指定することができません。
そこで、登場するのが、alignmentGuide となるわけです。
例えば、以下のような 独自alignment を定義して使うことができます。
extension HorizontalAlignment {
struct Field: AlignmentID {
static func defaultValue(in d: ViewDimensions) -> CGFloat {
d[.leading]
}
}
static let field = HorizontalAlignment(Field.self)
}
この定義は、水平方向の alignment として、新しく、”field” というものを定義しています。水平方向の alignment なので、VStack の alignment として指定することができます。
この例でいうと、外側の Stack に対して、新しい alignment を定義したことになります。
言い換えますと、共通の親ビューでの指定で、alignment を使って揃えることができます。
この場合で、2つある TextField を以下のように揃えようとすると、
以下のようなコードになります。
struct ContentView: View {
@State private var name = ""
@State private var mail = ""
var body: some View {
VStack(alignment: .field) {
HStack() {
Text("Name")
.border(Color.blue)
TextField("name", text: $name)
.frame(maxWidth: 300)
.alignmentGuide(.field) { (d) -> CGFloat in return d[.leading]}
.border(Color.green)
}
HStack() {
Text("Mail")
.border(Color.blue)
TextField("mail", text: $mail)
.frame(maxWidth: 300)
.alignmentGuide(.field) { (d) -> CGFloat in return d[.leading]}
.border(Color.green)
}
}
.border(Color.red)
}
}
新しく作成した alignmentGuide を使って、TextFieldの先頭位置で合わせています。
このように alignmentGuide を使うと、Stackされている複数要素の途中の位置でも合わせることができます。
alignmentGuide で揃えることができない例
では、alignmentGuide で揃えることができないのは、どういう時かというと、Stack されている複数要素に対して、複数の位置合わせを行うことができません。
先の例でいうと、TextField 同士の先頭をあわせるだけでなく、Text 同士の先頭位置も合わせようとして、別の alignmentGuide を作ってもうまく動作しません。
extension HorizontalAlignment {
struct Title: AlignmentID {
static func defaultValue(in d: ViewDimensions) -> CGFloat {
d[.leading]
}
}
static let title = HorizontalAlignment(Title.self)
struct Field: AlignmentID {
static func defaultValue(in d: ViewDimensions) -> CGFloat {
d[.leading]
}
}
static let field = HorizontalAlignment(Field.self)
}
.field と .title の2つの alignment を使うためには、2つの VStack が必要になりますので、以下のようなコードになります。
struct ContentView: View {
@State private var name = ""
@State private var mail = ""
var body: some View {
VStack(alignment: .title) {
VStack(alignment: .field) {
HStack() {
Text("Name")
.alignmentGuide(.title) { (d) -> CGFloat in d[.leading]}
.border(Color.blue)
TextField("name", text: $name)
.frame(maxWidth: 300)
.alignmentGuide(.field) { (d) -> CGFloat in return d[.leading]}
.border(Color.green)
}
HStack() {
Text("Mail")
.alignmentGuide(.title) { (d) -> CGFloat in d[.leading]}
.border(Color.blue)
TextField("mail", text: $mail)
.frame(maxWidth: 300)
.alignmentGuide(.field) { (d) -> CGFloat in return d[.leading]}
.border(Color.green)
}
}
}
.border(Color.red)
}
}
上記のコードを実行すると、以下のようになります。
TextField しか揃えられていないのがわかるかと思います。
.title と .field の順番を入れ替えて(VStack の順番を入れ替えて)も以下のようになってしまいます。
つまり、.alignmentGuide に一番近い、該当方向のStack からのみ alignmentGuide は、有効になるということです。
# そこで使われなかった alignmentGuide は、無視されるということです。
Storyboard を使っていたときは、このような制約は非常に簡単に設定できたことを考えると、レイアウトの柔軟性という観点では、SwiftUI は、まだ改善途中に思えます。
まとめ: alignmentGuide でのできること/できないこと
上記でみたように、alignmentGuide を使うことで、Stackに一要素に対して、位置調整を行うことができます。
しかし、Stackに含まれる複数の要素に、それぞれ個別の位置調整を行うことは、alignmentGuide ではできません。
Sponsor Link