5

I am working with VStack to show an image (they will be several with different sizes from a json)

I need to show it to occupy the screen width (the width of the vstack and maintaining aspect ratio) and to be properly resized, respecting the height according to the width of the screen. I have tried in different ways, but I manage to display the image correctly.

My View is:

struct ContentView: View {
    var body: some View {

        VStack {

            GeometryReader { geometry in
                VStack {
                    Text("Width: \(geometry.size.width)")
                    Text("Height: \(geometry.size.height)")

                }
                    .foregroundColor(.white)

            }
                .padding()
                .frame(alignment: .topLeading)
                .foregroundColor(Color.white) .background(RoundedRectangle(cornerRadius: 10) .foregroundColor(.blue))
                .padding()

            GeometryReader { geometry in
                VStack {
                    Image("sample")
                    .resizable()

                     //.frame(width: geometry.size.width)
                     .aspectRatio(contentMode: .fit)

                }
                    .foregroundColor(.white)

            }

                .frame(alignment: .topLeading)
                .foregroundColor(Color.white) .background(RoundedRectangle(cornerRadius: 10) .foregroundColor(.blue))
                .padding()



        }
        .font(.title)

    }
}

When I use .frame (width: geometry.size.width) by assigning the width of the geometry, the width is shown to the entire screen but the height is not maintaining aspect ratio. (looks crushed)

How can I get the dimensions of the image and find its proportion to use it with .aspectRatio (myratio, contentMode: .fit)

There is another way to display the image correctly, any suggestions

image

Mario Burga
  • 997
  • 10
  • 15
  • Not sure if this would work, but have you tried setting the width to `.infinite`? – dfd Sep 12 '19 at 17:37
  • yes witth: `.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: Alignment.topLeading)` It shows as the first option. With: `.frame(minWidth: 0, maxWidth: geometry.size.width, minHeight: 0, maxHeight: .infinity, alignment: Alignment.topLeading)` it doesn't work for me either – Mario Burga Sep 12 '19 at 17:42
  • One last idea - and I'll take a look later today to see if there's anything else. Have you tried changing the order of the modifiers? Particularly #2. (We all know that `resizable` *needs* to go before `aspectRatio`. Have you tried making `frame` be the *third* modifier? – dfd Sep 12 '19 at 18:36
  • Sorry, but this has me checking things out sooner than I thought I would. :-) I chose my own image, using an iPad Mini running iPadOS 13.1. In portrait it work **perfectly** as is. In landscape - because `aspectRatio` is set to`.fit` - the height of the image is respected and the blue border the sides is because the image aspect isn't set to fill. I'm not seeing any issues with your code posted. Are you able to try this on a real device or (at least) in the simulator? Maybe your issue is with `PreviewProvider`. – dfd Sep 12 '19 at 18:56
  • I have tried it on a real device (iphone XS Max) and it shows me the same as the emulator. – Mario Burga Sep 12 '19 at 19:35
  • Don't you need .aspectRatio(contentMode: .fill)` instead of `fit` if your image width is less than the VStack? – KB-YYZ Sep 12 '19 at 19:48
  • @KB-YYZ Width `fill` NOT maintaining aspect ratio. Vstack width is full but does not respect the ratio --> https://i.stack.imgur.com/VWWPk.jpg – Mario Burga Sep 12 '19 at 19:56
  • Ok, so let's give a specific example. A device that is 1000h x 1000w, and an image that is 500h x 200w. That is, a device that will, in `aspectFit` to use UIKit terms, be displayed normally as 1000h x 400w. Are you saying you want to size the image 2500h x 1000w? While that's doable, it's clearly *not* what aspect fit means. Also, you screen shots look to me to be `aspectFill`, which your code didn't exhibit. – dfd Sep 13 '19 at 02:18
  • @dfd The images come from a `json` and have different sizes. I have a list of events and clicking on it goes to a view called DetailsEvent.swift (here the image shown does not have the same dimensions, they vary). See gif: https://i.imgur.com/GZhACWH.gif – Mario Burga Sep 13 '19 at 02:27

1 Answers1

3

You need to eliminate the second GeometryReader, because having two children in the VStack that both accept as much space as they are offered is going to make the VStack unable to give the Image the correct amount of space.

You also need to raise the layout priority of the Image so that the VStack offers it space first, so it can take as much as it needs.

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            GeometryReader { geometry in
                VStack {
                    Text("Width: \(geometry.size.width)")
                    Text("Height: \(geometry.size.height)")
                }.foregroundColor(.white)
            }.padding()
                .background(
                    RoundedRectangle(cornerRadius: 10)
                        .foregroundColor(.blue))
                .padding()
            Image(uiImage: UIImage(named: "sample")!)
                .resizable()
                .aspectRatio(contentMode: .fit)
                .layoutPriority(1)
        }
    }
}

import PlaygroundSupport
let host = UIHostingController(rootView: ContentView())
host.preferredContentSize = .init(width: 414, height: 896)
PlaygroundPage.current.liveView = host

Result:

playground result

rob mayoff
  • 342,380
  • 53
  • 730
  • 766