I have this PDF that I'm converting to a UIImage and rendering in Image:
I need to make this image suitable for the dark color scheme of my app, so I would like the actual map to become black/dark-gray, but keep the colors intact.
If I overlay two images in a ZStack and play with blend modes and color invert I can get very close to the desired result:
ZStack {
Image(uiImage: self.mapImage!)
.resizable()
.scaledToFill()
.colorInvert()
.saturation(0)
.brightness(0.2)
Image(uiImage: self.mapImage!)
.resizable()
.scaledToFill()
.blendMode(.color)
}
But as you can see the yellow becomes a dark green.
While playing with color adjustments and blend modes in Affinity Design I found a combination that produces a quite good result, except for some text that becomes hard to read:
- layer 1: "Darken" blend mode
- layer 2: invert + black & white + "Add" blend mode
- layer 3: invert + black & white
My problem is that I can't find an "Add" blend mode in Swift, there's plusDarken and plusLighten but they do completely different things.
How can I achieve the result with Swift/SwiftUI?


What you're describing is to invert colors below some saturation threshold, while preserving all other colors. The nicest way to achieve this is with a small Metal
.colorEffectshader. The code is simple and extremely flexible to tune as you need.Here's the shader:
You can drop this into an Xcode project, and it'll automatically become available.
To use it, apply the
.colorEffectmodifier:The result will be:
This gets a bit messy in the "T" in "Tn". I believe that's mostly due to interpolation. Zooming the image up causes a bit of dithering, which is very bad for this algorithm. This should work much better if you can avoid scaling the image.
If you need to scale the image, and still keep crisp lines, you can explore a
.layerEffect. These can sample nearby pixels, which you could use to correct for dithering. For example, if most of the pixels surrounding this one are almost white, then this pixel should be white.