Bug: SwiftUI Metal shader doesn't respect alpha value in Dark Mode

Xcode 15.0.1, iOS 17.0.2

Problem: when drawing a shader in Dark mode, a SwiftUI shader does not use the alpha channel correctly.

This behaviour occurs even if the shader sets the alpha value to 0.

This occurs in the Xcode Canvas (Preview), the Xcode simulator and on device.

This behaviour occurs regardless of whether the original or new color is a Dynamic Color (e.g. .orange) or an absolute color (e.g. a hex value.)

Further... if I force-quit the app, set the device to Dark Mode, and relaunch the app, I very briefly see the desired result -- the globe shows on a dark background perhaps for 1 frame -- but then the globe is quickly replaced by a solidly filled rectangle (the globe's frame).

// SwiftUI view

struct ContentView: View {
    var body: some View {
        Image(systemName: "globe")
            .font(.system(size: 200))
            .foregroundStyle(.blue)
            .colorEffect(
                ShaderLibrary.replaceColor(.color(Color(hex: "ff8000")))
            )
    }
}


// Shader code

#include <metal_stdlib>
using namespace metal;


/// Given an existing color, replace it with a new color, but using the old color's alpha.

[[stitchable]] half4 replaceColor(float2 pos, half4 oldColor, half4 newColor) {
    return half4(newColor.r, newColor.g, newColor.b, oldColor.a);
}

Question: what am I missing here? My guess is that it has something to do with the alpha premultiplication. But how to fix this? Thank you for any help.

Light Mode

Dark Mode

I can't seem to edit my own post, so I need to clarify that this problem relates to the .colorEffect SwiftUI view modifier.

With distortionEffect, where no colorisation is performed, the image renders properly in both Light and Dark mode.

Same here. On macOS 14. So much time lost debugging the shader. :(

No solution?

I am also experiencing this issue. Did you ever find a solution?

Multiplying the new RGB value with the alpha value should fix this.

#include <metal_stdlib> using namespace metal;

[[stitchable]] half4 replaceColor(float2 pos, half4 oldColor, half4 newColor) {
    return half4(half3(newColor.r, newColor.g, newColor.b) * oldColor.a, oldColor.a);
}
Bug: SwiftUI Metal shader doesn't respect alpha value in Dark Mode
 
 
Q