NEW: Learn to build amazing SwiftUI apps for macOS with my new book! >>

SOLVED: extending ShapeStyle for adding colors instead of extending Color

Forums > 100 Days of SwiftUI

On day 40, in the Formatting our mission view video, Paul talks about extending ShapeStyle to add custom colors instead of extending Color. He says:

Color conforms to a bigger style called ShapeStyle and this allows us to use Colors, Gradients, Materials and more as if they were the same thing. This ShapeStyle protocol is what background uses. This allows us to use these colors everywhere SwiftUI expects a ShapeStyle to be given.

I don't really understand why he's saying extending ShapeStyle is better than extending color. Can someone clarify, maybe with an example?

This is his code:

extension ShapeStyle where Self == Color {
    static var darkBackground: Color {
        Color(red: 0.1, green: 0.1, blue: 0.2)
    }
    static var lightBackground: Color {
        Color(red: 0.2, green: 0.2, blue: 0.3)
    }
}

1      

There are places in your code where you just need a color. But in other places, like shapes, you want to fill the entire background with a color. Swift has made the Color class conform to the ShapeStyle protocols. So it's natural to give a Circle()shape a teal background like this.

// Give the circle a teal background. 
// Note! You are giving the background a ShapeStyle, not a 'color'
    Circle().background(.teal)

If you When you go all PRO and hire graphic artists for your applications, they'll want you to use company colors and insist you stay on brand. To make coding easier for you, you'll want to have convenience vars to address these special colors.

However, as @twostraws points out, not everthing takes a Color object. Sometimes, it requires a ShapeStyle object. His goal was to have you take another step UP the LADDER, and see more of SwiftUI's glorius innards and entrails.

Paste into Playgrounds

// Extending ShapeStyle vs Extending Color
// For Vince @vtabmow
// By: Obelix, 2022.01.29

import SwiftUI
import PlaygroundSupport

struct ShapeStyleTest: View {
    var body: some View {
        VStack {
            // These work because VStack accepts colors as Views
            Color.darkBackground
            Color.vinceBackground
            // These work because the .background() modifier
            // accepts ShapeStyles as backgrounds
            Circle().background(Color.vinceBackground) // Color conforms to ShapeStyle
            Circle().background(.teal)
            // This does NOT WORK.
            // Compiler does not know what .vinceBackground is?
            // Error: Type 'ShapeStyle' has no member 'vinceBackground'
            Circle().background(.vinceBackground)
        }
        .frame(height: 370)
    }
}

extension Color {
    // Extend Color class with a new vince color!
    // When you find the error, cut and paste this color to the ShapeStyle extension
    static var vinceBackground: Color { Color( red: 0.74, green: 0.01, blue: 0.98) }
}

extension ShapeStyle where Self == Color {
    static var darkBackground: Color { Color(red: 0.1, green: 0.1, blue: 0.2) }
}

PlaygroundPage.current.setLiveView(ShapeStyleTest().frame(width: 300, height: 400))

1      

I see the differences you are mentioning from your examples.

Since the two lines below produce the same results, it doesn't seem like much is gained, but maybe I'm still missing something. Thanks for the examples.

Circle().background(.vinceBackground)
Circle().background(Color.vinceBackground)
extension Color {
    static var vinceBackground: Color { Color( red: 0.74, green: 0.01, blue: 0.98) }
}

extension ShapeStyle where Self == Color {
    static var vinceBackground: Color { Color( red: 0.74, green: 0.01, blue: 0.98) }
}

struct ContentView: View {
    var body: some View {
        VStack {
            Circle().background(.vinceBackground)
            Circle().background(Color.vinceBackground)
        }
    }
}

1      

Well, they are not the same object are they?

Change the red value of one of your vinceBackground colors and run again.

It's a matter of convenience. One is a Color, the other is a ShapeStyle.

struct ContentView: View {
    var body: some View {
        VStack {
            Circle().background(.vinceBackground)  // This is a ShapeStyle.
            Circle().background(Color.vinceBackground) // This is a Color used as a ShapeStyle, not as Swifty? 
        }
    }
}

If you are OK with calling out Color.vinceBackground, then there is no issue. But if you recognize the benefit of extending the ShapeStyle, then you can use vinceBackground as BOTH a Color and a ShapeStyle in a natural way.

1      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Spend less time managing in-app purchase infrastructure so you can focus on building your app. RevenueCat gives everything you need to easily implement, manage, and analyze in-app purchases and subscriptions without managing servers or writing backend code.

Get Started

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.