1
votes

I am trying to constrain image size between min and max. But the view expands both width and height to their max values, removing the original aspect ratio.

import SwiftUI

struct ImageConstrainSizeTest: View {
    var body: some View {
        Image("bike")
            .resizable()
            .aspectRatio(contentMode: .fit)
            .border(Color.yellow, width: 5)
            .frame(minWidth: 10, maxWidth: 300, minHeight: 10, maxHeight: 300, alignment: .center)
            .border(Color.red, width: 5)
    }
}

struct ImageConstrainSizeTest_Previews: PreviewProvider {
    static var previews: some View {
        ImageConstrainSizeTest()
    }
}

In the screenshot below, I want the red box to shrink to yellow box.

Bounding boxes around image and frame

Tried using GeometryReader, but that gives the opposite effect of expanding the yellow box to red box.

Any thoughts?

1

1 Answers

1
votes

Here is a demo of possible approach - using view preferences and a bit different layout order (we give area to fit image with aspect and then by resulting image size constrain this area).

Demo prepared & tested with Xcode 11.7 / iOS 13.7

enter image description here

struct ViewSizeKey: PreferenceKey {
    typealias Value = CGSize
    static var defaultValue: CGSize = .zero
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value = nextValue()
    }
}

struct ImageConstrainSizeTest: View {
    @State private var size = CGSize.zero
    var body: some View {
        Color.clear
            .frame(width: 300, height: 300)
            .overlay(
                Image("img")
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .border(Color.yellow, width: 5)
                    .background(GeometryReader {
                        Color.clear.preference(key: ViewSizeKey.self,
                            value: $0.size) })
            )
            .onPreferenceChange(ViewSizeKey.self) {
                self.size = $0
            }
            .frame(maxWidth: size.width, maxHeight: size.height)
            .border(Color.red, width: 5)
    }
}