SwiftUI连分数递归视图性能问题排查与优化咨询
问题原因
- 深层递归的视图嵌套:每一层连分数都会创建新的
HStack、多个VStack和Text,当列表长度达到7-8时,视图树的深度和节点数会急剧膨胀。SwiftUI的布局系统需要递归计算每个子视图的尺寸与位置,深层嵌套会导致布局计算量暴增,直接阻塞UI线程。 - 不必要的数组切片开销:每次递归都通过
Array(continuedFraction[1...])创建新数组,虽然单步开销不大,但多层递归累加后会增加内存分配和数据拷贝的负担。
优化方案
方案1:迭代构建单视图(推荐)
放弃递归视图结构,通过字符串拼接结合富文本格式,把整个连分数渲染成一个独立的Text视图,彻底避免深层嵌套:
import SwiftUI struct ContinuedFractionView: View { let continuedFraction: [Int] private var fractionContent: AttributedString { guard continuedFraction.count > 1 else { return AttributedString(String(continuedFraction[0])) } var result = AttributedString("\(continuedFraction[0]) + ") var denominatorSegments = continuedFraction[1...].map { String($0) } // 从后往前构建分母的嵌套结构 var denominatorStr = denominatorSegments.removeLast() while let segment = denominatorSegments.popLast() { denominatorStr = "\(segment) + 1/\(denominatorStr)" } // 给分数部分添加上下标格式 let fraction = AttributedString("1/\(denominatorStr)") if let slashRange = fraction.range(of: "/") { fraction[..<slashRange.lowerBound].font = .system(size: 12) fraction[..<slashRange.lowerBound].baselineOffset = 6 fraction[slashRange.upperBound...].font = .system(size: 12) fraction[slashRange.upperBound...].baselineOffset = -6 } result.append(fraction) return result } var body: some View { Text(fractionContent) .font(.title) } } struct ContinuedFractionView_Previews: PreviewProvider { static var previews: some View { ContinuedFractionView(continuedFraction: [1,2,3,4,5,6,7,8,9]) } }
方案2:Canvas自定义绘制
如果需要更精确的排版控制(比如分数线对齐),可以用SwiftUI的Canvas直接绘制整个连分数结构,完全规避视图嵌套的性能问题:
import SwiftUI struct ContinuedFractionView: View { let continuedFraction: [Int] let fontSize: CGFloat = 20 let lineWidth: CGFloat = 1 var body: some View { Canvas { context, size in guard !continuedFraction.isEmpty else { return } var currentX: CGFloat = 10 let baselineY = size.height / 2 // 绘制第一个数字 drawText(String(continuedFraction[0]), at: CGPoint(x: currentX, y: baselineY), context: context) currentX += textWidth(String(continuedFraction[0])) + 5 // 迭代绘制后续的"+ 1/..."结构 for num in continuedFraction[1...] { // 绘制"+" drawText("+", at: CGPoint(x: currentX, y: baselineY), context: context) currentX += textWidth("+") + 5 // 绘制分子"1"(上标) let numeratorY = baselineY - fontSize * 0.6 drawText("1", at: CGPoint(x: currentX, y: numeratorY), context: context) currentX += textWidth("1") + 2 // 绘制分数线 let lineStartY = baselineY - fontSize * 0.3 let lineEndY = baselineY + fontSize * 0.3 context.stroke( Path { path in path.move(to: CGPoint(x: currentX - textWidth("1") - 2, y: lineStartY)) path.addLine(to: CGPoint(x: currentX - textWidth("1") - 2, y: lineEndY)) }, with: .black, lineWidth: lineWidth ) // 绘制当前分母数字 drawText(String(num), at: CGPoint(x: currentX, y: baselineY), context: context) currentX += textWidth(String(num)) + 5 } } .frame(height: fontSize * 2) } private func drawText(_ text: String, at point: CGPoint, context: GraphicsContext) { let attrString = AttributedString(text) context.draw(attrString, at: point, anchor: .leading) } private func textWidth(_ text: String) -> CGFloat { AttributedString(text).size().width } } struct ContinuedFractionView_Previews: PreviewProvider { static var previews: some View { ContinuedFractionView(continuedFraction: [1,2,3,4,5,6,7,8,9]) } }
方案3:递归结构优化(效果有限)
如果一定要保留递归逻辑,可以通过精简视图容器、缓存切片结果来小幅提升性能,但无法从根本解决深层嵌套的问题:
- 用
@ViewBuilder合并冗余的VStack - 提前缓存数组切片,避免重复创建
- 给递归视图添加固定尺寸约束,减少布局重计算
内容的提问来源于stack exchange,提问作者daewonyoon
相关产品推荐
相关产品推荐

