【SwiftUI】画像(Image)のレイアウト方法まとめ

f:id:bamboohero:20210531004042p:plain

SwiftUIで画像をレイアウトする方法についてまとめました。



画像のレイアウトは4つのメソッドの組み合わせで決まる

SwiftUIで画像をレイアウトする際は、以下の4つのメソッドを使用します。


レイアウトを実装する際は、メソッドの呼び出し順に注意します。基本的には以下の順番で呼ぶことになると思います。

Image("landscape")
    .resizable()  // [1]
    .aspectRatio(contentMode: .fill)  // [2]
    .frame(width: 366, height: 200)  // [3]
    .clipped()  // [4]

[1]
画像がビューのサイズに収まるようにします。これを指定しないと画像が大きい場合画面いっぱいに画像が表示されることになってしまいます。ほとんどのケースでこのメソッドを呼ぶのではないかと思います。

[2]
画像の拡大・縮小方法を指定します。fitfillを指定します。

[3]
画像を配置するビューのサイズを指定します。

[4]
画像がビューのサイズより大きい場合に、はみ出している部分をカットします。


各レイアウト方法の比較

レイアウト方法を比較すると以下のようになります。

fill fill + clipped fit
f:id:bamboohero:20210531002130p:plain:w300 f:id:bamboohero:20210531002104p:plain:w300 f:id:bamboohero:20210531002207p:plain:w300


上図で使用している画像の実際のサイズは1920x1440の横長です。
いずれのレイアウトでも画像のアスペクト比はオリジナルのまま維持されています。

fillの場合、画像の横幅を画面いっぱいに拡大した結果、高さがビューの領域をはみ出しています。

fill + clippedの場合、ビューからはみ出た部分がカットされています。

fitの場合、画像がビューの領域をはみ出さないように縮小されています。


実装の全体は以下のとおりです。

struct ContentView: View {
    var body: some View {
        VStack() {
            HStack {
                Text("ワーキングスペース")
                Spacer()
            }

            VStack(alignment: .leading, spacing: 0) {
                // この部分の実装を変えて比較しています
                Image("bg")
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(width: 366, height: 200, alignment: .top)
                    .clipped()

                HStack {
                    VStack(alignment: .leading) {
                        HStack {
                            Text("Hours: ")
                            Text("10:00-18:00")
                        }
                        HStack {
                            Text("Tel: ")
                            Text("xxx-xxxx-xxxx")
                        }
                    }
                    Spacer()
                }
                .padding(6)
                .foregroundColor(Color.white)
                .background(Color.gray)
            }

            Spacer()
        }
        .padding(12)
    }
}


参考

  • Fitting Images into Available Space
    • 画像レイアウトについての公式の解説です。わかりやすくまとまっているので一読をオススメします。
  • Aspect Fill, Aspect Fit, Scale to Fillの違い
    • UIKitでの画像のレイアウト方法について、図でわかりやすくまとめられています。この記事にはいつもお世話になっていて、これのSwiftUI版が欲しいと思って書いたのが本記事です。