Closure containing control flow statement cannot be used with result builder 'CommandsBuilder'
Asked Answered
O

1

5

I've made a button style to create circular neumorphic buttons in my app as follows:

struct NeumorphicCircleButtonStyle: ButtonStyle {
    var startPoint: UnitPoint
    var endPoint: UnitPoint
    var padding: CGFloat?
    var bgColor: Color?
    var bgColorOffset: Color?
    
    func makeBody(configuration: Configuration) -> some View {
        
        configuration.label
            .padding(padding ?? 30)
            .contentShape(Circle())
            .background(
                Group{
                    if configuration.isPressed {
                        Circle()
                            .fill(bgColor ?? Color.backgroundColor)
                            .overlay(
                                Circle()
                                    .stroke(Color.black.opacity(0.7), lineWidth: 4)
                                    .blur(radius: 4)
                                    .offset(x: 2, y: 2)
                                    .mask(Circle().fill(LinearGradient(colors: [Color.black, Color.clear], startPoint: startPoint, endPoint: endPoint)))
                            )
                            .overlay(
                                Circle()
                                    .stroke(bgColorOffset ?? Color.backgroundColorOffset, lineWidth: 8)
                                    .blur(radius: 4)
                                    .offset(x: -2, y: -2)
                                    .mask(Circle().fill(LinearGradient(colors: [Color.clear, Color.black], startPoint: startPoint, endPoint: endPoint)))
                            )
                    } else {
                        Circle()
                            .fill(bgColor ?? Color.backgroundColor)
                            .shadow(color: Color.black.opacity(0.25), radius: 10, x: 10, y: 10)
                            .shadow(color: bgColorOffset ?? Color.backgroundColorOffset.opacity(0.7), radius: 10, x: -5, y: -5)
                    }
                }
            )
    }
    
    // TODO: this will change the x y on the first set and need to make for other x y sets for direction, but can't find a good place for the logic, can't call it from in the makebody anywhere
    func getXoffetInnerShadow() -> Int {
        switch endPoint {
        case .bottomTrailing:
            return 2
        case .bottomLeading:
            return -2
        case .topTrailing:
            return 2
        case .topLeading:
            return -2
        case .leading:
            return -2
        case .trailing:
            return 2
        default:
            return 0
        }
    }
    
    func getYoffsetInnerShadow() -> Int {
        switch endPoint {
        case .bottomTrailing:
            return 2
        case .bottomLeading:
            return 2
        case .bottom:
            return 2
        case .topTrailing:
            return -2
        case .topLeading:
            return -2
        case .top:
            return -2
        default:
            return 0
        }
    }
    
    func getXoffetInnerHighlight() -> Int {
        switch endPoint {
        case .bottomTrailing:
            return -2
        case .bottomLeading:
            return 2
        case .topTrailing:
            return -2
        case .topLeading:
            return -2
        case .leading:
            return 2
        case .trailing:
            return -2
        default:
            return 0
        }
    }
    
    func getYoffsetInnerHighlight() -> Int {
        switch endPoint {
        case .bottomTrailing:
            return -2
        case .bottomLeading:
            return -2
        case .bottom:
            return -2
        case .topTrailing:
            return 2
        case .topLeading:
            return 2
        case .top:
            return 2
        default:
            return 0
        }
    }
    
    func getXoffsetShadow() -> Int {
        switch endPoint {
        case .bottomTrailing:
            return 10
        case .bottomLeading:
            return -10
        case .topTrailing:
            return 10
        case .topLeading:
            return -10
        case .leading:
            return -10
        case .trailing:
            return 10
        default:
            return 0
        }
    }
    
    func getYoffsetShadow() -> Int {
        switch endPoint {
        case .bottomTrailing:
            return 10
        case .bottomLeading:
            return 10
        case .bottom:
            return 10
        case .topTrailing:
            return -10
        case .topLeading:
            return -10
        case .top:
            return -10
        default:
            return 0
        }
    }
    
    func getXoffsetHighlight() -> Int {
        switch endPoint {
        case .bottomTrailing:
            return -10
        case .bottomLeading:
            return 10
        case .topTrailing:
            return -10
        case .topLeading:
            return 10
        case .leading:
            return 10
        case .trailing:
            return -10
        default:
            return 0
        }
    }
    
    func getYoffsetHighlight() -> Int {
        switch endPoint {
        case .bottomTrailing:
            return -10
        case .bottomLeading:
            return -10
        case .bottom:
            return -10
        case .topTrailing:
            return 10
        case .topLeading:
            return 10
        case .top:
            return 10
        default:
            return 0
        }
    }
}


    // this is how I init my gradient if you want a working example
extension LinearGradient {
    init(_ colors: Color..., startPoint: UnitPoint, endPoint: UnitPoint) {
        self.init(gradient: Gradient(colors: colors), startPoint: startPoint, endPoint: endPoint)
    }
}

I was attempting to change the offsets of my two shadows and highlights based off the gradient endpoint passed to the style so that the inner and outer glows and shadows are always on the appropriate sides of the button. However, trying to add a switch case to the offset values or function calls to return the appropriate values spits out a Closure containing control flow statement cannot be used with result builder 'CommandsBuilder' compile time exception.

Is there any way to set the offset values of my shadows and highlights based off the UnitPoint given to the Style for direction, or do I have to make a button style for every possibility (which seems like an unfeasible expectation)?

Any and all help or suggestions is and are greatly appreciated!

Oneill answered 23/8, 2021 at 20:57 Comment(1)
There is no apparent use of CommandsBuilder in the code you posted. On what line is the compiler flagging the error?Orpiment
B
7

Xcode failed to produce real error that's why he says about CommandsBuilder.

Generally when you face such problem, try to comment your code block by block to localize the problem.

In this case if you comment if .. { } else { } and left both parts of if there(just to build it), Xcode will be able to tell actual problem


In this case LinearGradient doesn't have init(colors: ..., so both lines

.mask(Circle().fill(LinearGradient(colors: [Color.black, Color.clear], startPoint: startPoint, endPoint: endPoint)))

Should be replaced with:

.mask(Circle().fill(LinearGradient(gradient: Gradient(colors: [Color.black, Color.clear]), startPoint: startPoint, endPoint: endPoint)))

If you wanna use defined below functions, like getXoffetInnerShadow, at a parameter to .offset modifier, you should replace return type to CGFloat, because that's the only type .offset accepts.

Brandling answered 24/8, 2021 at 4:4 Comment(6)
My apologies. The block of code I posted works absolutely fine. I'm using an extension on linear gradient for that, which I'll update my question with. The actual question is how can I set the values of x: and y: in my offsets using the functions I've defined below my makeBody? Or is there anyway to set those vars based off of the direction of the gradient I make with the start and end UnitPoints?Oneill
@TheN3wbie .offset(x:, y:) cannot accept Int, just replace return type to CGFloatBrandling
I tried wrapping the calls in CGFloat(), but that didn't work can you not cast Int to CGFloat?Oneill
@TheN3wbie If you comment if .. { } else { } and left both parts of if there(just to build it), Xcode will be able to tell actual problem.Brandling
@TheN3wbie wrapping should work, but why don't you change the return type of your functions? It's much cleanerBrandling
Marking as accepted because changing the functions to return CGFloat does resolve the issue. Xcode could use better error reporting.Oneill

© 2022 - 2024 — McMap. All rights reserved.