[SwiftUI] SwiftUI の layout システムを理解する (準備編: border と GeometryReader )

SwiftUI2021

SwiftUI のレイアウトを説明します。準備として、レイアウトを確認する手法を構築します。

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Monterery beta 4
  • Xcode 13 beta4
  • iOS 15 beta4

SwiftUI の レイアウト

SwiftUI で表示要素をレイアウトするための要素は、様々な種類があります。

レイアウト要素として VStack, HStack, ZStack, LazyVGrid 等の、どのように子要素を並べるかを指定するための要素がその1つです。

別の要素として、.frame, .scaleEffect 等の 自分のサイズを指定/変更するための要素があります。

SwiftUI では、グラフィカルなツールを使って配置するのではなく これらの要素を組み合わせてレイアウトしていきますので、それぞれがどのように連携してレイアウトをするのか 理解することが大切です。

次回以降では、代表的な要素を使用して、SwiftUI でのレイアウトがどのように行われていくのかを説明します。

今回は、そのための環境を確認していきます。

注意
以降の説明は、Apple のドキュメントから読み取れることだけではなく、実際に試行した結果からの推測を含みます。

おかしな点・不明な点は、指摘いただけると助かります。

レイアウトを確認していくための準備1:表示要素の矩形を表示する

SwiftUI では、表示要素のサイズとして 矩形(長方形) を使用して要素をレイアウトしていきます。この矩形は bouding frame と呼ばれます。

SwiftUI で 要素がどのようにレイアウトされているのかを確認するには、この 矩形 を表示してみるのがわかりやすいです。

表示要素の矩形を表示

要素の 矩形 を表示するための view modifier として、.border が用意されています。

Apple のドキュメントは、こちら

例えば、以下のようにすると Text に対して その矩形を表示させることができます。


Text("Hello World")
  .border(Color.red, width: 1.5)
HelloWorldWithBorder
HelloWorldWithBorder
幅のある border は、どこに描画される?
border で表示される線には、幅があります。では、border で表示される線は、実際の矩形に対してどの位置に表示されるのでしょうか?

border で指定して表示される線は、Bounding Frame の内側に表示されます。

つまり、そのままの border 指定では、ビューの表示要素にオーバーラップして表示されることがあるということです。


Text("Purple border inside the view bounds")
    .border(Color.purple, width: 4)
border
border

padding を使用すると、ビューの表示要素に被らないようになります。(線の幅が 4 なので、padding に 4 を指定することで、オーバーラップしないようにしています。)


Text("Purple border outside the view bounds.")
    .padding(4)
    .border(Color.purple, width: 4)
with padding(4)
with padding(4)

レイアウトを確認していくための準備2:App に記述することで、struct の階層をなくす

SwiftUI の View は、子要素として別定義された View を持つことができます。

一定の大きさのビュー構成を 別の View として定義することは、複雑なビューを構成するときやビューを再利用するときには便利ですが、
レイアウトを理解しようとするときには、不必要に複雑になってしまいます。

以降の説明では、App の中に直接記述します、つまり、Scene の中の WindowGroup 内に直接 表示要素を記述していき、レイアウトを確認していきます。

例えば、先ほどの Text に対して border を指定する時も以下のようにして、確認しました。


//
//  LayoutApp.swift
//
//  Created by : Tomoaki Yagishita on 2021/07/29
//  © 2021  SmallDeskSoftware
//

import SwiftUI

@main
struct LayoutApp: App {
    var body: some Scene {
        WindowGroup {
            Text("Hello World")
                .border(Color.red, width: 1.5)
        }
    }
}
WindowGroup には、1つのビューのみ入れられる
WindowGroup に続く closure に含めることができるのは、1つのビューのみです。複数並べても期待する動作にならないことに気をつけましょう。

なお、Apple のドキュメントは、こちら

レイアウトを確認していくための準備2:実際に使用される矩形の確認

徐々に説明していきますが、.frame で指定した数値と表示に使用される矩形のサイズは異なることが多々あります。

実際に表示に使用される矩形のサイズを確認するためには、以下の方法で確認します。

.background という view modifier と GeometryReader, Color というビュー、1つの extension を使用していきます。


//
//  LayoutApp.swift
//
//  Created by : Tomoaki Yagishita on 2021/07/29
//  © 2021  SmallDeskSoftware
//

import SwiftUI

// (1)
extension View {
    public func debugPrint(_ value:Any) -> Self {
        print(value)
        return self
    }
}

@main
struct LayoutApp: App {
    var body: some Scene {
        WindowGroup {
            Text("Hello World")
                .border(Color.red, width: 1.5)
                // (2)
                .background(
                    GeometryReader{ geom in
                        Color.clear
                            .debugPrint("size: \(geom.size)")
                    })
        }
    }
}
コード解説
  1. (2)で使用される .debugPrint を View の extension として定義しています
  2. サイズを知りたいビューに対して、このコードを記述することで、そのビューの表示時のサイズが取得できます

上記のコードを実行すると、Xcode のコンソールに以下のように表示されます。


size: (88.0, 20.5)

この結果から "Hello World" が、88.0x20.5 というサイズで表示されていることが確認できます。

TextSize
TextSize
iPhone11 シミュレータで確認
上記の結果は、iPhone11 シミュレータで確認しています。使用する機種や設定によって異なる数値になることがあります。
詳細の理解は不要です
ここでは、どのように確認するかの手段を説明していますので、それぞれの意味を理解している必要はありません。

様々な箇所で実際の数値を知りたくなったときに、.background... というコードを追加することで、自分で測れるようになることが目的です。

ここまでで、 SwiftUI のレイアウトを確認していく準備ができました。

次回以降で、今回確認した環境を使って、SwiftUI のレイアウトシステムを説明していきます。

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です