Search code examples
swiftui

Use Color and Gradient as fill conditionally?


I have the following code:

struct TestContainer: View {
    @State var useGradient = false
    
    var body: some View {
        ZStack {
            Color.black
            
            Button {
                
                
            } label: {
                RoundedRectangle(cornerRadius: .infinity)
                    .fill(useGradient ? buttonGradient() as! Color : Color.green)
                    .overlay {
                        Text("Done")
                            .font(.system(size: 17, weight: .semibold))
                            .foregroundStyle(.white)
                        
                    }
                    .cornerRadius(.infinity)
                
            }
            .frame(height: 44)
            .padding(.horizontal, 40)
            
        }
    }
    
    private func buttonGradient() -> some View {
        
        return LinearGradient(
            stops: [
                Gradient.Stop(color: Color(red: 0.1, green: 0.2, blue: 0.2), location: 0.00),
                Gradient.Stop(color: Color(red: 0.1, green: 0.3, blue: 0), location: 1.00),
            ],
            startPoint: UnitPoint(x: 0, y: 1),
            endPoint: UnitPoint(x: 1, y: 0)
            )
    }
}

At times I get:

Could not cast value of type 'SwiftUI.LinearGradient' (0x1f2bf7980) to 'SwiftUI.Color' (0x1f2c1b688).

How can I make this work where depending on the useGradient state the fill changes?


Solution

  • The suggestions in the comments provide a couple of ways of doing this.

    Another way would be to use a generic function that receives a ShapeStyle as parameter. This requires that the gradient is delivered as some ShapeStyle instead of as some View:

    struct TestContainer: View {
        @State var useGradient = false
    
        private func buttonLabel<F: ShapeStyle>(fillStyle: F) -> some View {
            RoundedRectangle(cornerRadius: .infinity)
                .fill(fillStyle)
                .overlay {
                    Text("Done")
                        .font(.system(size: 17, weight: .semibold))
                        .foregroundStyle(.white)
                }
                .cornerRadius(.infinity)
        }
    
        private var buttonGradient: some ShapeStyle {
            LinearGradient(
                stops: [
                    Gradient.Stop(color: Color(red: 0.1, green: 0.2, blue: 0.2), location: 0.00),
                    Gradient.Stop(color: Color(red: 0.1, green: 0.3, blue: 0), location: 1.00),
                ],
                startPoint: UnitPoint(x: 0, y: 1),
                endPoint: UnitPoint(x: 1, y: 0)
            )
        }
    
        var body: some View {
            ZStack {
                Color.black
    
                Button {
                    useGradient.toggle()
                } label: {
                    if useGradient {
                        buttonLabel(fillStyle: buttonGradient)
                    } else {
                        buttonLabel(fillStyle: .green)
                    }
                }
                .frame(height: 44)
                .padding(.horizontal, 40)
            }
        }
    }