Home > OS >  SwiftUI - Fill image of background cell keeping aspect ratio
SwiftUI - Fill image of background cell keeping aspect ratio

Time:08-28

In the wonderful world of SwiftUI, I have a View that I use as a cell. I intend to reproduce the Layout of a previous application of mine, with Autolayout, not SwiftUI, in which the background image filled the entire cell, adjusting to the width and losing pieces of image above and below.

enter image description here

In my new app, the code in SwiftUI is the following:

struct CharacterRow2: View {
    var character: Character 
    var body: some View {
        Text(character.name)
            .font(Font.custom(avengeanceHeroicAvengerNormal, size: 30))
            .foregroundColor(.white)
            .baselineOffset(-10)
            .shadow(color: .black, radius: 1, x: -1, y: 1)
            .frame(width: UIScreen.main.bounds.width, height: 140)
            .background {
                WebImage(url: extractImage(data: character.thumbnail))
                    .resizable()
                    .frame(width: UIScreen.main.bounds.width, height: 150)
            }
    }
}

With this code, my app looks like this:

enter image description here

I tried to add scaledToFill():

                WebImage(url: extractImage(data: character.thumbnail))
                    .resizable()
                    .scaledToFill()
                    .frame(width: UIScreen.main.bounds.width, height: 150)

But this is the result:

enter image description here

I'm stuck...

Thank you in advance!

CodePudding user response:

In this case, you are simply using too many frames. And using them incorrectly. You should avoid using UIScreen.main.bounds in SwiftUI, especially in something like a view cell. By doing this, the cell will not behave properly with other views, and can cause UI issues that would be difficult to trace.

The simplest way to get the behavior you want is this:

    Text(character.name)
        .font(.largeTitle)
        .foregroundColor(.white)
        .baselineOffset(-10)
        .shadow(color: .black, radius: 1, x: -1, y: 1)
        .frame(height: 140)
        .frame(maxWidth: .infinity) // use .infinity for max width to make it
                                    // as large as the space offered by the
                                    // parent view
        .background {
            WebImage(url: extractImage(data: character.thumbnail))
                .resizable()
                // .frame(width: UIScreen.main.bounds.width, height: 150) <-- Remove this frame altogether
                .scaledToFill()
        }
        .clipped // This keeps the image from being larger than the frame

This will size to be as wide as the parent view allows it to be. Leaving the UIScreen.main.bounds.width could cause it to be larger than the parent view and cause partial eclipsing.

Example

CodePudding user response:

In your last example the images are overlaying each other. This is due to calling scaleToFill(). The images are now ignoring their frame boundaries regarding the height. Adding .clipped solves the problem.

struct CharacterRow2: View {
    var character: String
    var body: some View {
        Text(character)
            .font(.largeTitle)
            .foregroundColor(.white)
            .baselineOffset(-10)
            .shadow(color: .black, radius: 1, x: -1, y: 1)
            .frame(width: UIScreen.main.bounds.width, height: 140)
            .background {
                Image(character)
                    .resizable()
                    .scaledToFill()
                    .frame(width: UIScreen.main.bounds.width, height: 150)
                    .clipped() // <-- Add this
            }
    }
}

It seems this will work only with a ForEach inside a ScrollView. Using a List seems to breack the vertical frame boundary.

struct ContentView: View{
    
    let content = ["1.jpg", "2.jpg", "3.jpg" ]
    
    var body: some View{
        //These look really weird
//        List(content, id: \.self){ name in
//            CharacterRow2(character: name)
//        }
        
//        List{
//            VStack{
//                ForEach(content, id: \.self){ name in
//                    CharacterRow2(character: name)
//                }
//            }
//        }
        
        //working
        ScrollView{
            VStack{
                ForEach(content, id: \.self){ name in
                    CharacterRow2(character: name)
                        .padding()
                }
            }
        }
    }
}

Result: enter image description here

  • Related