0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

画像のアスペクト比を指定して拡大させたい(AspectFill)

Last updated at Posted at 2025-01-30

TL;DR

こんな感じのを作ったよ

AspectFillImageView(image: image, aspectRatio: 4/3)

スクリーンショット 2025-01-30 14.58.35.png

scaledToFillとかaspectRatioでよくね?

まず、前提として横幅とアスペクト比がすでにわかっているViewにならscaledToFillaspectRatioのみで実装可能です。

ただ、実際に書いていて横幅や縦幅が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とかびっくりした

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?