Bartłomiej Semańczyk
Bartłomiej Semańczyk

Reputation: 61832

How do I setup ZStack with Image filled to the widget bounds?

My code looks like this:

    ZStack {
        
        NetworkImage(url: url)
        VStack(alignment: .trailing, spacing: 5) {
            Spacer()
            Text("this is my quite long title here")
                    .font(.title)
                    .bold()
                    .foregroundColor(.white)
                    .lineLimit(nil)
                    .multilineTextAlignment(.trailing)
            Text("subtitle this is my long long subtitle to create multiply lines")
                    .font(.caption)
                    .foregroundColor(.white)
                    .lineLimit(nil)
                    .multilineTextAlignment(.trailing)
        }
        .padding()
    }

struct NetworkImage: View {
    let url: URL?
    var body: some View {
        if let url = url, let imageData = try? Data(contentsOf: url), let uiImage = UIImage(data: imageData) {
            Image(uiImage: uiImage)
                .resizable()
                .scaledToFill()
        }
    }
}

But this looks bad and it is not what I need t achieve.

I need to make it aligned to the right and to the bottom with some padding from the edges. That is all. How can I do this?

enter image description here enter image description here

After update from @user1046037 answer it looks like:

enter image description here enter image description here

Upvotes: 1

Views: 883

Answers (2)

user1046037
user1046037

Reputation: 17715

Code Changes:

1. ZStack Alignment

ZStack(alignment: Alignment(horizontal: .trailing, vertical: .bottom)) {

2. Crop in the center

In NetworkImage, center crop (code provided) in the image extension.

Thanks to @vacawama How to center crop an image in SwiftUI

Complete Code:

struct ContentView: View {
    var body: some View {
        ZStack(alignment: Alignment(horizontal: .trailing, vertical: .bottom)) {
            
            NetworkImage(url: URL(string: "https://unsplash.com/photos/KZv3Rb_OIXI/download?force=true"))
            VStack(alignment: .trailing, spacing: 5) {
                Spacer()
                Text("this is my quite long title here")
                        .font(.title)
                        .bold()
                        .foregroundColor(.white)
                        .lineLimit(nil)
                        .multilineTextAlignment(.trailing)
                Text("subtitle this is my long long subtitle to create multiply lines")
                        .font(.caption)
                        .foregroundColor(.white)
                        .lineLimit(nil)
                        .multilineTextAlignment(.trailing)
            }
            .padding()
        }
    }
}

//Center Crop
extension Image {
    func centerCropped() -> some View {
        GeometryReader { geo in
            self
            .resizable()
            .scaledToFill()
            .frame(width: geo.size.width, height: geo.size.height)
            .clipped()
        }
    }
}

struct NetworkImage: View {
    let url: URL?
    var body: some View {
        if let url = url, let imageData = try? Data(contentsOf: url), let uiImage = UIImage(data: imageData) {
            Image(uiImage: uiImage)
                .centerCropped()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Upvotes: 2

Adam
Adam

Reputation: 5135

The ZStack with the Image in it is the problem. The ZStack isn’t cropping the Image, it’s actually growing to fit the image and extending outside the bounds of the widget. The Text is inside that same ZStack, so it too grows to fill the available space.

Maybe this helps visualize the issue (black round-rect is the widget frame): widget content with black frame showing where its cropped

Solution

Put the image in the VStack's .background instead:

var body: some View {

    VStack(alignment: .trailing, spacing: 5) {
        Spacer()
        Text("this is my quite long title here")
            .font(.title)
            .bold()
            .foregroundColor(.white)
            .lineLimit(nil)
            .multilineTextAlignment(.trailing)
        Text("subtitle this is my long long subtitle to create multiply lines")
            .font(.caption)
            .foregroundColor(.white)
            .lineLimit(nil)
            .multilineTextAlignment(.trailing)
    }
    .padding()
    .frame(maxWidth: .infinity, maxHeight: .infinity)
    .background(NetworkImage(url: url))

}

struct NetworkImage: View {
    let url: URL?
    var body: some View {
        if let url = url, let imageData = try? Data(contentsOf: url), let uiImage = UIImage(data: imageData) {
            Image(uiImage: uiImage)
                .resizable()
                .scaledToFill()
        }
    }
}

Upvotes: 3

Related Questions