Jesper Bertelsen
Jesper Bertelsen

Reputation: 31

getHue from .accentColor swiftui always returns blue hsb values

I am trying to make a make a gradient background, which takes the current .accentColor and modifies its HSB values into a newValue to be the endPoint. The accentColor when called returns the default blue, instead of the current red color.

struct Graph: View {
    var body: some View {
        VStack {
            GraphView()
        }.accentColor(.red)
    }
}

private struct GraphView: View {
    var enhancedAccentColor: Color {
        var (h, s, b, a): (CGFloat, CGFloat, CGFloat, CGFloat) = (0.0, 0.0, 0.0, 0.0)
        UIColor(Color.accentColor).getHue(&h, saturation: &s, brightness: &b, alpha: &a)

        let color = UIColor(hue: h, saturation: s, brightness: b, alpha: a)
        return Color(color)
    }

    var gradient: Gradient { Gradient(colors: [.accentColor,
                                               enhancedAccentColor]) }
    var body: some View {
        ZStack {}.frame(maxWidth: .infinity, maxHeight: 400)
            .background(
                RoundedRectangle(cornerRadius: 8)
                    .fill(LinearGradient(gradient: gradient,
                                         startPoint: UnitPoint(x: 0.0, y: 0.0),
                                         endPoint: UnitPoint(x: 1.0, y: 1.0)))
            )
    }
}

The enhancedAccentColor works fine when using other preset Color values.

Upvotes: 2

Views: 661

Answers (2)

Klaas
Klaas

Reputation: 22763

Your example works fine for me without any changes as of Xcode 13.0 (13A233).

BUT: I found out that Color.accentColor is always returning the default blue color if you read the color values "too early".

@main
struct AccentColorTestApp: App {
    init() {
        // By adding these two lines your example stops working
        // and draws a solid blue rectangle:

        var (h, s, b, a): (CGFloat, CGFloat, CGFloat, CGFloat) = (0.0, 0.0, 0.0, 0.0)
        UIColor(Color.accentColor).getHue(&h, saturation: &s, brightness: &b, alpha: &a)
    }

    var body: some Scene {
        WindowGroup {
            GraphView()
        }
    }
}

I have the same issue of loosing my custom accent color when starting Microsoft's AppCenter too early.

Upvotes: 0

rob mayoff
rob mayoff

Reputation: 385600

You don't have access to the “real” accent color. Depending on what you want to do to the accent color, I might be able to suggest other ways to get the same visual effect. But you didn't explain what effect you want.

Color.accentColor returns a special placeholder representing the accent color:

  1> import SwiftUI
  2> print(Color.red)
red
  3> print(Color.accentColor)
AccentColorProvider()

When it's time to actually fill in pixels with the accent color, SwiftUI looks looks up the current accent color based on accentColor modifiers, asset catalogs, the color scheme, etc.

But when you're creating your UIColor, it's not pixel-filling time, so SwiftUI hasn't set up the mechanism for getting the correct accent color. You get the default accent color instead.

The accentColor modifier stores the accent color in the environment. It's defined like this:

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension View {
  @available(iOS 13.0, macOS 11.0, tvOS 13.0, watchOS 6.0, *)
  @inlinable public func accentColor(_ accentColor: SwiftUI.Color?) -> some SwiftUI.View {
        return environment(\.accentColor, accentColor)
    }
  
}

But unfortunately, Apple doesn't think you deserve access to that environment property. The accentColor property is declared like this:

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension EnvironmentValues {
  @usableFromInline
  internal var accentColor: SwiftUI.Color? {
    get
    set
  }
}

Because it's declared internal, you can't use it.

UPDATE

What i am trying to accomplish is for the gradient to start with the accentColor and end with the accentColor with a lower saturation.

Perhaps you can get a satisfactory effect by using this modifier:

extension View {
    @ViewBuilder
    func saturationFade(_ amount: Double) -> some View {
        ZStack {
            self
            self
                .saturation(amount)
                .mask(
                    LinearGradient(
                        colors: [.clear, .white],
                        startPoint: .top, endPoint: .bottom
                    )
                )
        }
    }
}

Example:

struct ExampleView: View {
    var body: some View {
        ZStack {
            Text("hello")
        }
        .frame(width: 100, height: 80)
        .background(
            RoundedRectangle(cornerRadius: 8)
                .fill(Color.accentColor)
                .saturationFade(0.5)
        )
    }
}

PlaygroundPage.current.setLiveView(
    HStack(spacing: 8) {
        ExampleView()
            .accentColor(.red)

        ExampleView()
            .accentColor(.green)


        ExampleView()
            .accentColor(.orange)
    }
)

Result:

three rounded rectangles showing fades from red, green, and orange to partially desaturated red, green, and orange

Upvotes: 2

Related Questions