TL;DR
こんな感じのを作ったよ
AspectFillImageView(image: image, aspectRatio: 4/3)
scaledToFillとかaspectRatioでよくね?
まず、前提として横幅とアスペクト比がすでにわかっているViewにならscaledToFill
やaspectRatio
のみで実装可能です。
ただ、実際に書いていて横幅や縦幅がpxで決まっている状況ってほとんどなくないですか?
親のサイズに合わせて可変なことがほとんどかと思います。
じゃあ、横幅が決まってない状態でFillさせるには?
image
.resizable()
.scaledToFill()
その上で、アスペクト比は固定したい場合は?
image
.resizable()
.aspectRatio(16/9, contentMode: .fill)
image
.resizable()
.scaledToFill()
.aspectRatio(16/9, contentMode: .fill)
この辺でプレビューにおかしな画像が表示されていたらあなたも同じ悩みを抱えた仲間です。
AspectFillImageView
これをコピペして使ってください
import SwiftUI
import Foundation
/// Aspect比を一定にしながらscaledToFillするためView
public struct AspectFillImageView: View {
private let image: Image
private let aspectRatio: CGFloat
@State private var calculatedSize: CGSize?
public init(image: Image, aspectRatio: CGFloat) {
self.image = image
self.aspectRatio = aspectRatio
}
public var body: some View {
image
.aspectFill(aspectRatio: aspectRatio)
.onPreferenceChange(SizePreferenceKey.self) { size in
calculatedSize = size
}
.frame(width: calculatedSize?.width, height: calculatedSize?.height)
}
}
private extension Image {
func aspectFill(aspectRatio: CGFloat) -> some View {
GeometryReader { proxy in
let parentSize = proxy.size
let parentAspectRatio = parentSize.width / parentSize.height
let width: CGFloat
let height: CGFloat
if parentAspectRatio > aspectRatio {
width = CGFloat(parentSize.width)
height = CGFloat(parentSize.width) / aspectRatio
} else {
width = CGFloat(parentSize.height) * aspectRatio
height = CGFloat(parentSize.height)
}
return self
.resizable()
.scaledToFill()
.frame(width: width, height: height)
.clipped()
.preference(key: SizePreferenceKey.self, value: CGSize(width: width, height: height))
}
}
}
private struct SizePreferenceKey: PreferenceKey {
static let defaultValue: CGSize? = nil
static func reduce(value: inout CGSize?, nextValue: () -> CGSize?) {
value = nextValue()
}
}
#Preview {
ScrollView {
VStack {
AspectFillImageView(image: Image(.dummy), aspectRatio: 1)
AspectFillImageView(image: Image(.dummy), aspectRatio: 4/3)
AspectFillImageView(image: Image(.dummy), aspectRatio: 2)
}
.padding(.horizontal, 30)
}
}
余談
Appleって絶対これ使うしあるでしょってやつ全然ないよね
FlowLayoutとかびっくりした