I can't work out how to dynamically filter (color filter) images that are being loaded. Eg I want to dynamically increase the saturation.
Filtering an image in SwiftUI looks straight forward using [CI library
] (https://www.hackingwithswift.com/books/ios-swiftui/basic-image-filtering-using-core-image).
But I cant work out how to get a Core Image object from Async Image to be manipulated.
I would imagine it working something like this:
AsyncImage(url: URL(string: "https://example.com/icon.png")) { image in
Image(uiImage: myFilter(image.uiImage.ciImage).uiImage).resizable()
} placeholder: {
ProgressView()
}
.frame(width: 50, height: 50)
Or maybe Image could be extended?
AsyncImage
can't give you a UIImage
, but it is relatively easy to write your own AsyncImage
that gives you a UIImage
.
struct AsyncUIImage<Content: View, Placeholder: View>: View {
let url: URL
let contentBuilder: (UIImage) -> Content
let placeholder: Placeholder
@State private var content: Content?
init(url: URL, @ViewBuilder content: @escaping (UIImage) -> Content, @ViewBuilder placeholder: () -> Placeholder) {
self.url = url
self.contentBuilder = content
self.placeholder = placeholder()
}
var body: some View {
if let content {
content
} else {
placeholder
.task {
do {
let (data, _) = try await URLSession.shared.data(from: url)
if let uiImage = UIImage(data: data) {
content = contentBuilder(uiImage)
}
} catch {
print(error)
}
}
}
}
}
Example usage:
var body: some View {
AsyncUIImage(url: URL(string: "https://picsum.photos/200")!) { uiImage in
VStack {
if let ciImage = CIImage(image: uiImage) {
processImage(ciImage)
}
Image(uiImage: uiImage)
}
} placeholder: {
ProgressView()
}
}
func processImage(_ ciImage: CIImage) -> Image? {
let context = CIContext()
let filter = CIFilter.sepiaTone()
filter.inputImage = ciImage
filter.intensity = 1
guard let output = filter.outputImage else { return nil }
guard let cgImage = context.createCGImage(output, from: output.extent) else { return nil }
return Image(uiImage: UIImage(cgImage: cgImage))
}
Also note that the effects of some CIFilter
s can be achieved using SwiftUI view modifiers, such as blur
, colorInvert
, hueRotation
, etc. For these filters, you can just apply them to the Image
produced by AsyncImage
.