SwiftUI自定义拟态按钮样式触发CommandsBuilder控制流报错如何解决?
解决方案
报错原因
你遇到的编译报错是因为SwiftUI的ViewBuilder结果构建器不允许在View构建闭包中直接执行非View返回类型的控制流代码,你之前的尝试是把switch逻辑或者偏移量计算直接写在了Group的View构建闭包里,所以触发了限制。
可行实现方案
方案1:提前计算偏移量(兼容你现有的get方法)
只需要把所有偏移量的计算逻辑移到makeBody方法最开头,先计算好所有需要的偏移值存储为局部常量,再在View构建代码中直接引用即可,修改后的makeBody代码如下:
func makeBody(configuration: Configuration) -> some View { // 提前计算所有偏移量,不放在ViewBuilder闭包里就不会触发编译错误 let innerShadowX = CGFloat(getXoffetInnerShadow()) let innerShadowY = CGFloat(getYoffsetInnerShadow()) let innerHighlightX = CGFloat(getXoffetInnerHighlight()) let innerHighlightY = CGFloat(getYoffsetInnerHighlight()) let shadowX = CGFloat(getXoffsetShadow()) let shadowY = CGFloat(getYoffsetShadow()) let highlightX = CGFloat(getXoffsetHighlight()) let highlightY = CGFloat(getYoffsetHighlight()) return 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: innerShadowX, y: innerShadowY) .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: innerHighlightX, y: innerHighlightY) .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: shadowX, y: shadowY) .shadow(color: bgColorOffset ?? Color.backgroundColorOffset.opacity(0.7), radius: 10, x: highlightX, y: highlightY) } } ) }
方案2:动态映射偏移量(更简洁,支持任意自定义方向)
你不需要枚举所有UnitPoint的情况,直接利用UnitPoint自带的x、y属性(取值范围0~1,(0,0)为左上,(1,1)为右下)映射为偏移量即可,代码更简洁,还支持用户传入任意自定义的方向点,你可以直接删除所有getXXX偏移量计算方法,修改makeBody代码如下:
func makeBody(configuration: Configuration) -> some View { // 直接通过endPoint的x、y计算偏移,0.5是中心基准点 let xBase = (endPoint.x - 0.5) * 2 let yBase = (endPoint.y - 0.5) * 2 let innerShadowX = xBase * 1 let innerShadowY = yBase * 1 let innerHighlightX = xBase * -1 let innerHighlightY = yBase * -1 let shadowX = xBase * 5 let shadowY = yBase * 5 let highlightX = xBase * -5 let highlightY = yBase * -5 return 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: innerShadowX, y: innerShadowY) .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: innerHighlightX, y: innerHighlightY) .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: shadowX, y: shadowY) .shadow(color: bgColorOffset ?? Color.backgroundColorOffset.opacity(0.7), radius: 10, x: highlightX, y: highlightY) } } ) }
内容的提问来源于stack exchange,提问作者theZ3r0CooL
相关产品推荐
相关产品推荐

