skip to Main Content

I am trying to align an image with a textbox in a Swift application.
How do I get this to look correct?

This is the version of code I have that is funky. I’ve tried a lot of things and I need help, please.

ForEach(listing.items, id: .self) { i in
                VStack(alignment: .leading, spacing: 6) {
                    Image(i.itemImageUrl)
                        .resizable()
                        .scaledToFit()
                        .overlay(
                            ZStack {
                                Text("$ (i.priceOfItem)")
                                    .font(.body)
                                    .padding(5)
                                    .foregroundColor(.white)
                            }
                                .background(Color.black)
                                .opacity(0.7)
                                .cornerRadius(12)
                                .padding(10),
                            alignment: .bottomTrailing)
                   
                        Text("Description: (i.itemDescription)")
                        .frame(width: .infinity, alignment: .topLeading).padding(8)
                            .background(Color.gray.opacity(0.2))
                            .font(.body)
                }
                .padding()

enter image description here

I am aiming for the alignment on the left, but when the code renders within another view there’s a weird white space.

2

Answers


  1. Did you checked if maybe the image itself has a white space?

    VStack(alignment: .leading, spacing: 6) {
            Image(systemName: "folder.fill")
                .resizable()
                .scaledToFit()
                .overlay(alignment: .bottomTrailing) {
                    Text(1300.75, format: .currency(code: "USD"))
                        .font(.body)
                        .padding(5)
                        .foregroundColor(.white)
                        .background(Color.red)
                        .opacity(0.7)
                        .cornerRadius(12)
                        .padding([.trailing, .bottom], 20)
                }
                .border(Color.black, width: 1) // This border shows if the image is actually as big as you want it to be
            Text("Description: A large black folder")
                .frame(alignment: .topLeading).padding(8)
                .background(Color.gray.opacity(0.2))
                .font(.body)
        }
        .padding()
    
    Login or Signup to reply.
  2. If you want the images to fill the full width then you could use ViewThatFits to choose between scaled-to-fill and scaled-to-fit:

    • when an image has an aspect ratio taller that is than the screen, it is scaled to fill
    • otherwise, when an image has an aspect ratio that is wider than the screen, it is scaled to fit.
    ViewThatFits(in: .horizontal) {
        let itemImage = Image(i.itemImageUrl)
        itemImage
            .resizable()
            .scaledToFill()
        itemImage
            .resizable()
            .scaledToFit()
    }
    

    You might also want to wrap the ForEach with a ScrollView.

    Your example code is using a deprecated version of the .overlay modifier. The modifiers .foregroundColor and .cornerRadius are also deprecated, use .foregroundStyle and .clipShape instead. And… you don’t need to nest the Text inside a ZStack. So I would suggest changing the overlay to the following:

    .overlay(alignment: .bottomTrailing) {
        Text("$ (i.priceOfItem)")
            .font(.body)
            .padding(5)
            .foregroundStyle(.white)
            .background(Color.black)
            .opacity(0.7)
            .clipShape(RoundedRectangle(cornerRadius: 12))
            .padding(10)
    }
    

    That just leaves the labels below the images. You might have noticed, your existing code was giving errors in the console:

    Invalid frame dimension (negative or non-finite).

    This is because, .infinity is not valid as a width. However, it is valid as maxWidth, this is the only change needed here:

    Here is the fully-updated example:

    ScrollView {
        ForEach(listing.items, id: .self) { i in
            VStack(alignment: .leading, spacing: 6) {
                ViewThatFits(in: .horizontal) {
                    let itemImage = Image(i.itemImageUrl)
                    itemImage
                        .resizable()
                        .scaledToFill()
                    itemImage
                        .resizable()
                        .scaledToFit()
                }
                .overlay(alignment: .bottomTrailing) {
                    Text("$ (i.priceOfItem)")
                        .font(.body)
                        .padding(5)
                        .foregroundStyle(.white)
                        .background(Color.black)
                        .opacity(0.7)
                        .clipShape(RoundedRectangle(cornerRadius: 12))
                        .padding(10)
                }
                Text("Description: (i.itemDescription)")
                    .frame(maxWidth: .infinity, alignment: .topLeading)
                    .padding(8)
                    .background(Color.gray.opacity(0.2))
                    .font(.body)
            }
            .padding()
        }
    }
    

    Screenshot

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search