SwiftUI Charts中.chartSymbolScale()换图形引发编译器超时求助
SwiftUI chartSymbolScale 混合Shape编译超时问题解决
问题描述
在SwiftUI中设置图表符号图例时,全部使用Circle的代码可以正常运行:
.chartSymbolScale([ literals[0].0: Circle().strokeBorder(lineWidth: 2.0), literals[1].0: Circle().strokeBorder(lineWidth: 2.0), literals[2].0: Circle().strokeBorder(lineWidth: 2.0), literals[3].0: Circle().strokeBorder(lineWidth: 2.0), literals[4].0: Circle().strokeBorder(lineWidth: 2.0) ])
但将最后一个图形改为Rectangle后,编译器抛出超时错误:
The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
报错代码:
.chartSymbolScale([ literals[0].0: Circle().strokeBorder(lineWidth: 2.0), literals[1].0: Circle().strokeBorder(lineWidth: 2.0), literals[2].0: Circle().strokeBorder(lineWidth: 2.0), literals[3].0: Circle().strokeBorder(lineWidth: 2.0), literals[4].0: Rectangle().strokeBorder(lineWidth: 2.0) ])
原因分析
问题出在Swift的类型推断机制上:
- 当所有元素都是
Circle().strokeBorder(...)时,编译器能轻松推断出数组的统一类型(Circle的strokeBorder返回的是特定的View子类)。 - 混入
Rectangle().strokeBorder(...)后,两者返回的是不同的具体视图类型,编译器需要尝试为数组找到一个共同的父类型,但这个过程涉及大量类型检查,超出了编译器的合理处理时间,最终抛出超时错误。 - 虽然
Circle和Rectangle都遵循Shape协议,但strokeBorder方法返回的是实现了View的具体类型,而非统一的Shape类型,所以编译器无法快速确定数组的元素类型。
解决方法
方案1:显式指定数组类型为AnyChartSymbol
AnyChartSymbol是SwiftUI提供的类型擦除容器,专门用于统一不同图表符号的类型,直接告诉编译器数组的元素类型,避免复杂推断:
// 替换YourLiteralKeyType为literals元素的键类型(比如String/Int等) .chartSymbolScale([ literals[0].0: Circle().strokeBorder(lineWidth: 2.0).chartSymbol, literals[1].0: Circle().strokeBorder(lineWidth: 2.0).chartSymbol, literals[2].0: Circle().strokeBorder(lineWidth: 2.0).chartSymbol, literals[3].0: Circle().strokeBorder(lineWidth: 2.0).chartSymbol, literals[4].0: Rectangle().strokeBorder(lineWidth: 2.0).chartSymbol ] as [YourLiteralKeyType: AnyChartSymbol])
方案2:拆分表达式,单独定义符号变量
把每个符号提取成独立变量,减少编译器单次需要处理的复杂表达式,降低类型推断压力:
// 提前定义每个符号 let symbol0 = Circle().strokeBorder(lineWidth: 2.0) let symbol1 = Circle().strokeBorder(lineWidth: 2.0) let symbol2 = Circle().strokeBorder(lineWidth: 2.0) let symbol3 = Circle().strokeBorder(lineWidth: 2.0) let symbol4 = Rectangle().strokeBorder(lineWidth: 2.0) // 再传入chartSymbolScale .chartSymbolScale([ literals[0].0: symbol0, literals[1].0: symbol1, literals[2].0: symbol2, literals[3].0: symbol3, literals[4].0: symbol4 ])
方案3:用AnyView统一类型(通用型方案)
如果不想依赖AnyChartSymbol,可以用AnyView做类型擦除,将所有视图统一为AnyView类型:
.chartSymbolScale([ literals[0].0: AnyView(Circle().strokeBorder(lineWidth: 2.0)), literals[1].0: AnyView(Circle().strokeBorder(lineWidth: 2.0)), literals[2].0: AnyView(Circle().strokeBorder(lineWidth: 2.0)), literals[3].0: AnyView(Circle().strokeBorder(lineWidth: 2.0)), literals[4].0: AnyView(Rectangle().strokeBorder(lineWidth: 2.0)) ])
内容的提问来源于stack exchange,提问作者Edward Hasted
相关产品推荐
相关产品推荐

