はじめに
SwiftUI楽しいです!むらおです!
LazyVGrid内で、画像のアスペクト比を保ったまま画像のViewの比率を指定したい!ただ、frameモディファイアは使いたくない!という状況に陥り、実装に手こずったので備忘録として残しておきます!
想定する画面
以下のようなデザインを想定しています。
- 画像のアスペクト比は1:1
- 画像の枠のアスペクト比は2:1(横:縦 = 2:1)
- 見切れるのは構わない → scaleはfill

- 使用する画像はこちら
- かわいい

案1(ボツ)
ImageにaspectRatio
を指定し、frameモディファイアで無理やり縛る
LazyVGrid(
columns: .init(repeating: .init(.flexible()), count: 2),
spacing: 12
){
ForEach(1...10, id: \.self) { _ in
VStack(alignment: .leading, spacing: 4) {
Text("画像")
Image("catImage")
.resizable()
.aspectRatio(1, contentMode: .fill)
.frame(width: 140, height: 70)
.clipShape(RoundedRectangle(cornerRadius: 4))
}
.padding()
.border(Color.gray)
}
}
一見上手くいくように見えますが、frame
を指定しているので端末によって見え方が異なってしまいます。
例えば、iPadで見るとかなり小さく見えます。(frame
指定なしだと画像がはみ出てしまいます)

これを回避するためにはGeometryReader
を使ってセルの幅や高さを計算することになりそうです。
(めんどくさいのでなし)
う〜〜ん、不採用!
案2(採用)
LazyVGrid(
columns: .init(repeating: .init(.flexible()), count: 2),
spacing: 12
){
ForEach(1...10, id: \.self) { _ in
VStack(alignment: .leading, spacing: 4) {
Text("画像")
RoundedRectangle(cornerRadius: 4)
.aspectRatio(2, contentMode: .fit)
.overlay {
Image("catImage")
.resizable()
.scaledToFill()
}
.clipShape(RoundedRectangle(cornerRadius: 4))
}
.padding()
.border(Color.gray)
}
}
RoundedRectangle
にアスペクトを指定し、overlay
でImage
を重ね、はみ出た部分をカットする方法でうまく行きました。
clipShape
やclipped
を使うことではみ出た部分をカットできますね。overlay
につけるのがミソです。(Image
の下につけると上手くいかない)
今回はRoundedRectangle
を使用しましたが、Color
でも同じことが可能だと思います。
今回の実装だと、columns
のcountを変えるだけで色々レイアウトを変更できるので、かなり良く見えるのではないでしょうか
採用!

