SwiftUI: ViewModifier where content is an Image
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())
thank's to the comments and discussion with Asperi, I finally used the following code snippet. Basically, it's an implementation of ViewModifier
, specialised for Images.
protocol ImageModifier {
/// `Body` is derived from `View`
associatedtype Body : View
/// Modify an image by applying any modifications into `some View`
func body(image: Image) -> Self.Body
}
extension Image {
func modifier<M>(_ modifier: M) -> some View where M: ImageModifier {
modifier.body(image: self)
}
}
Using it is simple:
struct MyImageModifier: ImageModifier {
func body(image: Image) -> some View {
image.resizable().scaledToFit()
}
}
struct MyView: View {
var body: some View {
Image(systemName: "play").modifier(MyImageModifier())
}
}
I'm not 100% satisfied, because the modifier must be defined either to return some View
or to return an Image
. Both cases have disadvantages and do not perfectly integrate with SwiftUI.
When defining the ImageModifier
to return an Image
it reduces the possibilities of modifying the image to only the image-specific modifiers (actually resizable()
) and when defining it to return some View
I can't chain ImageModifier
s as the second modifier must be a ViewModifier
.