Reputation: 31
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
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
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.
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:
Upvotes: 2