2
votes

No Public API in SwiftUI to response for the resizable modifier of View protocol. Only Image in SwiftUI could work with .resizable(). Custom UIView like UIView for GIF is not resizable now.

I use SDWebImageSwiftUI AnimatedImage, which is backing UIKit View SDAnimatedImageView. AnimatedImage is not response to .resizable(), .scaleToFit, .aspectRatio(contentMode: .fit), etc. WebImage is backing SwiftUI Image, so it's working fine.

import SwiftUI
import SDWebImageSwiftUI

struct ContentView: View {
    let url = URL(string: "https://media.giphy.com/media/H62DGtBRwgbrxWXh6t/giphy.gif")!
    var body: some View {
        VStack {
            AnimatedImage(url: url)
                .scaledToFit()
                .frame(width: 100, height: 100)
            WebImage(url: url)
                .scaledToFit()
                .frame(width: 100, height: 100)
        }
    }
}

Not sure if it's an Apple bug. Expect custom view like SDWebImageSwiftUI AnimatedImage is responsive to SwiftUI size related modifiers like .scaledToFit().

Related issue: https://github.com/SDWebImage/SDWebImageSwiftUI/issues/3

2
You can use .scaledToFit on an View because it is possible that an Image is somewhere embedded in a child. But ViewRepresentable does not have that chance, so you will probably have to implement a method for yourself to set the appropriate aspect ratio (returning the modified view from it). I have not seen any way to retrieve the aspect ratio from EnvrionmentValues. - Fabian
The problem is AnimatedImage(url: url) does not have .resisable() modifier. - FRIDDAY
I try to use scaledToFit on Rectangle and VStack, but no effect either. - Maundytime

2 Answers

6
votes

SwiftUI uses the compression resistance priority and the content hugging priority to decide what resizing is possible.

If you want to resize a view below its intrinsic content size, you need to reduce the compression resistance priority.

Example:

func makeUIView(context: Context) -> UIView {
    let imageView = UIImageView(image: UIImage(named: "yourImage")!)
    imageView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
    imageView.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
    return imageView
}

This will allow you to set .frame(width:height:) to any size you want.

1
votes

Finally found a solution.

Make a UIView wrapper outside of the SDAnimationImageView or UIImageView, then override layoutSubviews() set the frame of subview.

Here is full code by me.

And SDWebImageSwiftUI also release a new version which uses wrapper to solve this problem.

class ImageModel: ObservableObject {
    @Published var url: URL?
    @Published var contentMode: UIView.ContentMode = .scaleAspectFill
}

struct WebImage: UIViewRepresentable {
    @ObservedObject var imageModel = ImageModel()

    func makeUIView(context: UIViewRepresentableContext<WebImage>) -> ImageView {
        let uiView = ImageView(imageModel: imageModel)
        return uiView
    }

    func updateUIView(_ uiView: ImageView, context: UIViewRepresentableContext<WebImage>) {
        uiView.imageView.sd_setImage(with: imageModel.url)
        uiView.imageView.contentMode = imageModel.contentMode
    }

    func url(_ url: URL?) -> Self {
        imageModel.url = url
        return self
    }

    func scaledToFit() -> Self {
        imageModel.contentMode = .scaleAspectFit
        return self
    }

    func scaledToFill() -> Self {
        imageModel.contentMode = .scaleAspectFill
        return self
    }
}

class ImageView: UIView {
    let imageView = UIImageView()

    init(imageModel: ImageModel) {
        super.init(frame: .zero)
        addSubview(imageView)
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        imageView.frame = bounds
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}