12

I am trying to create a custom image modifier that uses the resizable() function, therefore my content should be an Image. At least, that what I think so.

Error Message: Type 'IconModifier' does not conform to protocol 'ViewModifier'

struct IconModifier: ViewModifier {
    func body(content: Image) -> some View {
        content
            .resizable()
            .aspectRatio(contentMode: .fit)
            .frame(width: 10, height: 10, alignment: .center)
    }
}
Pondorasti
  • 607
  • 6
  • 14

1 Answers1

26

Solution

You can just extend Image instead.

Used like so:

struct ContentView: View {

    var body: some View {
        VStack {
            Text("Hello World!")
            
            Image("someName")
                .imageIconModifier()
        }
    }
}

Image extension:

extension Image {
    
    func imageIconModifier() -> some View {
        self
            .resizable()
            .aspectRatio(contentMode: .fit)
            .frame(width: 10, height: 10, alignment: .center)
    }
}

Why your solution wouldn't work

You first got the error:

Type 'IconModifier' does not conform to protocol 'ViewModifier'

This means that your IconModifier did not satisfy the requirements for the ViewModifier protocol, which if we see the definition:

func body(content: Self.Content) -> Self.Body

whereas you had:

func body(content: Image) -> Self.Body

which doesn't work. So next - we will change that to Content rather than Image:

Value of type 'IconModifier.Content' (aka '_ViewModifier_Content') has no member 'resizable'

This is basically saying that there is no .resizable() modifier for any View. The .resizable() modifier is inside defined inside only extension Image, and so no other View has this modifier.

And that is the core problem - the can't access the original View (Image for what we want) through the content parameter in a ViewModifier.

This is why I just made an extension for Image.

Hope this explanation helps!

Community
  • 1
  • 1
George
  • 25,988
  • 10
  • 79
  • 133
  • Thanks for the answer, but I was also looking a bit into why my solution from above isn't working. What's actually happening under the hood that is triggering an error? – Pondorasti Feb 02 '20 at 16:05
  • 2
    @Pondorasti Added an explanation, hope it helps :) – George Feb 02 '20 at 16:14
  • Good answer, but it leaves one big elephant in the room. If it's impossible to implement a ViewModifier that only works on Images then how was .resizable() itself implemented? – dented42 Feb 26 '21 at 18:38
  • @dented42 The built in modifiers are just either an extension of `View`, or an extension of a specific type (in this case it is `Image`). I don't know of any built-in modifiers which are a `ViewModifier`. A `ViewModifier` is a struct conforming to the `ViewModifier` protocol, so you can then use `.modifier(_:)` to apply it. – George Feb 26 '21 at 18:44
  • @dented42 That was a good question though - it made me think, what is the point in `ViewModifier`, if SwiftUI doesn't often use it itself? [Here is a question](https://stackoverflow.com/q/57411656/9607863) relating to that topic. You can also use it with `ModifiedContent`. – George Feb 26 '21 at 19:20