Just a coder
Just a coder

Reputation: 16730

How to make a custom Image resizable() with SwiftUI

I have a view

struct CustomImageView : View {
    var body: some View {
        Image("someImage")
    }
}

when i reference this view outside, i cannot make the image resizable. I know i can pass parameters, but i was looking for a neater way to do this through modifiers?

VStack {
   CustomImageView()
      .resizable()    // This does not work
}

resizable seems to only work with the image directly. Does anyone know how to make the CustomImageView resizable?

Update: I understand i can use a parameter, but is there anyway to do it using modifiers? Resizable was only one property, but there are other properties. And listing them all as indvidual variables will be too much

Implemented Answer based on @matteo-pacini It uses AlamofireImage to load a remote image in ImageView.

struct RemoteImage : View {
    var url: URLConvertible
    @ObjectBinding var imageLoader = AlamofireImageLoader()

    private (set) var _resizable: (capInsets: EdgeInsets, resizingMode: Image.ResizingMode) = (EdgeInsets(), .stretch)

    var body: some View {
        Image(uiImage: imageLoader.image)
            .resizable(capInsets: _resizable.capInsets, resizingMode: _resizable.resizingMode)
            .onAppear() {
                self.imageLoader.loadImage(url: self.url)
            }
    }

    func resizable(capInsets: EdgeInsets = EdgeInsets(), resizingMode: Image.ResizingMode = .stretch) -> RemoteImage {
        return RemoteImage(url: url, _resizable: (capInsets, resizingMode))
    }
}

class AlamofireImageLoader: BindableObject {
    var didChange = PassthroughSubject<Void, Never>()
    var image = UIImage() {
        didSet {
            DispatchQueue.main.async {
                self.didChange.send(Void())
            }
        }
    }

    convenience init(url: URLConvertible) {
        self.init()
        loadImage(url: url)
    }

    func loadImage(url: URLConvertible) {
        Alamofire.request(url).responseImage { response in
            if let image = response.result.value {
                self.image = image
            }
        }
    }
}

Upvotes: 7

Views: 3999

Answers (4)

LetsGoBrandon
LetsGoBrandon

Reputation: 564

Like @FlowUI.SimpleUITesting.com mentioned the other answers suppose that you will edit only one property modifier. What if you would add others?

Simply in your CustomImageView add :

private let modifiers: (Image) -> Image

init(modifiers: @escaping (Image) -> Image = { $0 }) {
    self.modifiers = modifiers
}

var body: some View {
    modifiers(Image("someImage"))
}

Then simply use this way :

CustomImageView(modifiers : {$0.resizable().whateverModiferThatReturnImage().anotherOne()})
  .frame(width: 200)
  .aspectRatio(contentMode: .fit)
  .asUsualModifiersThatReturnViews()

Upvotes: 2

SacredGeometry
SacredGeometry

Reputation: 911

Try this:

struct CustomImageView: View {

    @State var isResizeable: Bool = false
    @State var capInsets: EdgeInsets = EdgeInsets()
    @State var resizingMode: Image.ResizingMode = .stretch

    var body : some View {

        ZStack {
            if isResizeable {
                Image("placeholder")
                    .resizable(capInsets: self.capInsets, resizingMode: self.resizingMode)
            }
            else {
                Image("placeholder")
            }
        }
    }

    func resizeable(capInsets: EdgeInsets = EdgeInsets(), resizingMode: Image.ResizingMode = .stretch) -> CustomImageView {
        return CustomImageView(isResizeable: true, capInsets: capInsets, resizingMode: resizingMode)
    }
}

struct ContentView : View {

    var body: some View {
        CustomImageView(isResizeable: true)
    }
}

Upvotes: 1

Matteo Pacini
Matteo Pacini

Reputation: 22866

struct CustomImageView : View {

    private var isResizable = false

    var body: some View {
        let image = Image("someImage")
        return isResizable ? image.resizable() : image
    }

    func resizable() -> CustomImageView {
        return CustomImageView(isResizable: true)
    }
}

And use it like this.


struct ContentView: View {
    var body: some View {
        VStack {
            CustomImageView()
                .resizable()
        }
    }
}

Upvotes: 5

Tristan Newman
Tristan Newman

Reputation: 426

The problem is you are calling .resizable() on CustomeImageView, which is of type View.

You need to call .resizable() on an Image type.

Your Image is nested in body, that's within CustomImageView.

struct CustomImageView : View {
    var body: some View {
        Image("someImage")
            .resizable()
    }
}

CustomImageView()

The reason this works is, it calls .resizable on Image(), instead of a View, like before. Hope this helps!

References:

https://developer.apple.com/documentation/swiftui/image https://developer.apple.com/documentation/swiftui/view

Upvotes: 1

Related Questions