swiftswiftuiswift-protocols

SwiftUI: ViewModifier where content is an Image


I get an error "Type 'PlayButtonModifier' does not conform to protocol 'ViewModifier'" and I do not understand why and - even more important - how to do it right.

I simply try to create a ViewModifier for an Image, so that I can use for example .resizable() on it, which is only defined in Image

In the ViewModifier protocol, there's an Typealias for Content defined. My naiv thinking was that this should work:

struct PlayButtonModifier: ViewModifier {
    typealias Content = Image

    func body(content: Content) -> some View {
        content
    }
}

Well, no. Too easy. Same thing happens with implicit type alias for structs:

struct PlayButtonModifier: ViewModifier {
    func body(content: Image) -> some View {
        content
    }
}

Same error.

What is wrong here? How would it be correct?


Solution

  • In this case where the modification is specific to a particular view type, Image say, you could just add an extension on that view type directly:

    extension Image {
        func myImageModifier() -> some View {
            self
                .resizable()
                .aspectRatio(1.0, contentMode: .fit)
                .clipShape(Circle())
       }
    }
    

    A full playground text example follows. If you add a cute otter picture in your playground "Resources" folder named "Otter.png" you get a prettier result :)

    import PlaygroundSupport
    import SwiftUI
    
    let image = (UIImage(named: "Otter.png") ?? UIImage(systemName: "exclamationmark.square")!)
    
    struct ContentView: View {
        var body: some View {
            VStack {
                Text("hello world")
                Image(uiImage: image)
                    .myImageModifier()
            }
        }
    }
    
    extension Image {
        func myImageModifier() -> some View {
            self
                .resizable()
                .aspectRatio(1.0, contentMode: .fit)
                .clipShape(Circle())
        }
    }
    
    PlaygroundPage.current.liveView = UIHostingController(rootView: ContentView())